Spring boot:Authorization using header before controller - java

I am using my spring boot REST API with the controller as follows:-
`
#Autowired
HazelcastInstance hazelcastinstance;
String username="VAKSDNDDODM#DLDM#DMOD#DI##*#EK";
#RestController
#RequestMapping(value = "/Acode/availabile/multiple/{Code}/")
public class MultiController {
#Resource(name = "AService")
protected AImpl AService;
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Response> readMulti(
#RequestHeader(value="Auth-Token") String token,
#RequestBody XRequest Request,
#PathVariable String Code) throws Exception {
if(token.equalsIgnoreCase(username))
{
Response response = AService.readMultiX(Request, Code);
}
else
{
response.setMessage("Unauthorized access!");
response.setStatus(340);
}
return new ResponseEntity<Response>(response, HttpStatus.OK);
}
}
`
I wish to do the header comparision before API control enters the controller such that declaring #RequestHeader is not required in controller.Is it possible to do this or do we have an implementation for that?

Related

How to mock a method with custom annotation in Spring boot?

I have a a service which is protected by oAuth. This is the structure for my RestController:
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class MainController {
#PostMapping("/v1/api")
#CustomAuthorizer(RequestURI = "api", ResourceType = ResourceType.API, GrantAccess = GrantAccess.EXECUTE)
public ResponseEntity<List<Details>> api(
#ApiParam(value = "Provide here request body", required = true) #Valid #RequestBody Input input,
#Valid #RequestParam("tId") String tenantId, #RequestHeader("Authorization") String auth) {
}
This is my Test for the same
#RunWith(SpringRunner.class)
#SpringBootTest
public class MainControllerMockTest {
#WithMockUser( username = "test")
#Test
public void testApi() throws Exception {
Input input = new Input();
//Set input here
MvcResult result = mockMvc.perform(post("/v1/api?tId=test")
.content(inputJson).contentType(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsd2lsbGlhbXMxNiIsInJvbGVzIjoidXNlciIsImlhdCI6MTUxNDQ0OTgzM30.WKMQ_oPPiDcc6sGtMJ1Y9hlrAAc6U3xQLuEHyAnM1FU")
.with(SecurityMockMvcRequestPostProcessors.csrf()))
.andExpect(status().isOk()).andReturn();
System.out.println(result.getResponse().getContentAsString());
assertEquals(200, result.getResponse().getStatus());
}
}
Authentication is done by our custom oAuth library, So when /api is called it checks for permissions, is there any way i can bypass that ? I tried mocking that Code as well but it didn't work. Any suggestions would be great.

Spring returns 404 when using MethodValidationPostProcessor

I have a problem with my test Spring Boot app. It works just fine, but when I enable Spring validation by adding dependency etc and adding a #Configuration:
#Configuration
public class TestConfiguration {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
I get 404 for my test endpoint.
{
"timestamp": 1601507037178,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/test"
}
I've already applied some solutions/proposals from similar problems (eg here or here) but without a success.
Here is my code:
https://github.com/zolv/error-handling-test
API interface:
#Validated
public interface TestApi {
#PostMapping(
value = "/test",
produces = {"application/json"},
consumes = {"application/json"})
#ResponseBody
ResponseEntity<TestEntity> getTest(#Valid #RequestBody(required = false) TestEntity request);
}
TestEntity just to send something:
#Data
public class TestEntity {
#JsonProperty("test")
#NotNull
private String test;
}
Controller implementation:
#RestController
#RequiredArgsConstructor
#Validated
public class TestController implements TestApi {
#Override
#ResponseBody
public ResponseEntity<TestEntity> getTest(#Valid #RequestBody TestEntity request) {
return ResponseEntity.ok(request);
}
}
My controller advice:
#ControllerAdvice
public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {ConstraintViolationException.class})
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<String> handleValidationFailure(ConstraintViolationException ex) {
StringBuilder messages = new StringBuilder();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
messages.append(violation.getMessage());
}
return ResponseEntity.badRequest().body(messages.toString());
}
#Override
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body("problem");
}
}
Application:
#SpringBootApplication
#EnableWebMvc
#ComponentScan(basePackages = "com.test")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
A test I use, but it fails also using Postman:
#SpringJUnitConfig
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TestControllerTest {
#Autowired protected TestRestTemplate restTemplate;
#Test
void registrationHappyPath() throws Exception {
/*
* Given
*/
final TestEntity request = new TestEntity();
/*
* When/
*/
final ResponseEntity<String> response =
restTemplate.postForEntity("/test", request, String.class);
/*
* Then
*/
Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
final String body = response.getBody();
Assertions.assertNotNull(body);
}
}
If I comment out a TestConfiguration then everything works fine.
Thank You in advance for any help.
You should set MethodValidationPostProcessor#setProxyTargetClass(true) because by default MethodValidationPostProcessor uses JDK proxy which leads to loss of your controller in the Spring context.
When AbstractHandlerMethodMapping#processCandidateBean is called isHandler(Class<?> beanType) will return false because JDK proxy doesn't contain #RestController annotation.
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
mvProcessor.setProxyTargetClass(true);
return mvProcessor;
}

How to prevent creating HttpSession in Spring REST Controller

In our project we have implemented a few REST Services using Spring #RestController. The problem is when I test them using a REST Client, in response header there exists JSESSIONID, So I believe the server creates an HTTPSession for each request, but the services are stateless and they don't need HTTPSession.
Is there any way to prevent creating new sessions in this controllers?
This is the source of RestController
#RestController
#RequestMapping("/customs/customs")
public class CustomsRestController {
#Autowired
private CustomsWebService customsWebService;
#Autowired
private CustomsSecurityContextInitializer securityContextInitializer;
#RequestMapping(path = "/customsPorts", method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Collection<CustomsPort> getActiveCustomsPorts() {
try {
securityContextInitializer.initSecurityContext();
return customsWebService.getActiveCustomsPorts();
} finally {
securityContextInitializer.clearSecurityContext();
}
}
#RequestMapping(path = "/registerCustomsRequest", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CustomsDeclarationInfo registerCustomsRequest(#RequestBody CustomsDeclarationRequest requestKey) {
try {
securityContextInitializer.initSecurityContext();
requestKey.validate();
return customsWebService.registerCustomsRequest(requestKey);
} catch (BusinessException e) {
return CustomsDeclarationInfo.builder().errorMessage(e.getMessage()).build();
} finally {
securityContextInitializer.clearSecurityContext();
}
}
}
You can do this in your implementation of the WebSecurityConfigurerAdapter by setting the SessionCreationPolicy to STATELESS:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

How to use #RequestBody and #RequestParam together

I'm trying to use both #RequestBody and #RequestParam to send JSON and multiple files through Postman but it's not working. Is it possible to use both annotations in an API?
#RequestMapping(value = "/save/product/test", method = RequestMethod.POST)
public ResponseEntity<?> save(#Valid #RequestBody ProductVo productVo, #RequestParam("files") #NotNull #NotBlank MultipartFile[] uploadfiles) {
System.out.println("body " + productVo.toString());
for (MultipartFile file : uploadfiles) {
System.out.println(file.getOriginalFilename());
System.out.println(file.getContentType());
System.out.println(file.getName());
System.out.println(file.getSize());
}
return new ResponseEntity<APIResponse>(this.apiResponse, HttpStatus.NO_CONTENT);
}
#RequestParam takes parameter from uri, you are actually trying to achieve something else.
Here is an example controller takes json body and multipart file :
#RestController
#RequestMapping("/users")
public class UserController {
UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping({"/", ""})
public ResponseEntity<User> post(#RequestPart("request") UserCreateRequest request, #RequestPart("file") MultipartFile file) throws IOException {
String photoPath = UUID.randomUUID() + file.getOriginalFilename().replaceAll(" ", "").trim();
// other logic
return ResponseEntity.ok(userService.create(request));
}
}
You can ease your life by wrapping the multipart alongside the other fields:
class UploadContext {
private MultipartFile file;
private UserCreateRequest request;
// other fields
}
and use this object in the controller:
#PostMapping(value = "/upload")
public void upload(UploadContext context) {
// controller logic here
}
Doc: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart-forms
In your client code you have to clear the content-type from the headers:
headers: {'Content-Type': undefined}
Hope this helps.
Any number of files and a string param can be uploaded by having a MultipartHttpServletRequest and RequestParam.
One thing to be aware of: The MultipartHttpServletRequest will also hold all the request params, so technically you can even just have MultipartHttpServletRequest and parse it
Signature of Controller is:
public ResponseEntity<BulkUploadResponsePayload> filesAndJson(
#ApiParam(hidden = true) MultipartHttpServletRequest multipartRequest,
#RequestParam(value = "json-param",name = "json-param") String documentType) {
// multipartRequest will have all the files
// you can use json-param for any string
}

Adding custom Spring Security filter on controller method using annotation?

I have written a custom strong authorization server and libraries for integration called PowerAuth 2.0.
Currently, the developer who tries to secure the API call with it can use it as such:
#Controller
#RequestMapping(value = "/session")
public class AuthenticationController {
#Autowired
private PowerAuthAuthenticationProvider authenticationProvider;
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody String login(
#RequestHeader(value = "X-PowerAuth-Authorization", required = true) String signatureHeader,
HttpServletRequest servletRequest) throws Exception {
PowerAuthApiAuthentication apiAuthentication = authenticationProvider.validateRequestSignature(
servletRequest,
"/session/login",
signatureHeader
);
if (apiAuthentication != null && apiAuthentication.getUserId() != null) {
return "OK";
} else {
return "NOT OK";
}
}
}
I would like to simplify the work for the developer though, so that the code can look like this:
#Controller
#RequestMapping(value = "/session")
public class AuthenticationController {
#RequestMapping(value = "/login", method = RequestMethod.POST)
#PowerAuth(value = "/session/login")
public #ResponseBody String login(PowerAuthApiAuthentication apiAuthentication) throws Exception {
if (apiAuthentication != null && apiAuthentication.getUserId() != null) {
return "OK";
} else {
return "NOT OK";
}
}
}
Principle (probably?):
Remove the need for autowired authentication provider
Replace the signature verification call with a custom request filter
Bind the request filter to a custom annotation with parameters
Inject the resulting authentication object in a method parameter
Since I am not strong in Spring, could you please provide me a guidance on how to do this?

Categories