Validation of JWT token along with requestBody parameter in springBoot - java

I have an reactive spring boot application where I am having different controllers and all the controller having get, post, put, delete methods
GET and DELETE method URI format => /{userName}/{others}
and it's ensured that put and post method must have a field userid in their request body.
Also All the request having an authorization header.
And I already have a method called validate that accepts 2 parameters authorizationHeader and userName and returns true if this mapping exists false if not.
I am trying to write generic filter can filter incoming request and validate before going to controller.
How can I write this generic webfilter especially how to extract body from post request and validate requests.
I tried writing this
#Component
#Slf4j
public class ExampleWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ObjectMapper mapper = new ObjectMapper();
return serverWebExchange
.getRequest()
.getBody()
.next()
.flatMap(body -> {
try {
return validate(body, serverWebExchange
.geHeaders().get(0))
} catch (IOException e) {
return Mono.error(e);
}
})
.flatMap((boolean s) -> {
return webFilterChain.filter(serverWebExchange);
});
}
Mono<Boolean> validate(DataBuffer body, String Header){
//my logic to validate
}
}
But it seems it's hanging after this filter method executed. so my question is
How can I write webfilter which will read body and validate?
Is there any other generic solution available for this type of problem in spring-boot?

I think you should use Interceptors. You can intercept the http call, and make your validations on the request. You can do this as global or you can do this for specific endpoints/paths. Here is a example for your case.
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//make validations
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
//make validations
}
}
After this you need to register your interceptor like below.
#Component
public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {
#Autowired
ProductServiceInterceptor productServiceInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(productServiceInterceptor);
}
}
For more depth information you can visit the links below.
https://www.youtube.com/watch?v=agBadIAx0Wc
https://www.tutorialspoint.com/spring_boot/spring_boot_interceptor.htm

Related

Invoke Same Method for Multiple Services in Application

My application consists of multiple services.We had a requirement now that for every request coming in to our application we need to validate the token.
Current architecture of my application is such that every microservice has its own ServiceInterceptor class and in that class I am writing the logic in prehandle method to validate token recieved in request.
Service Interceptor Class.
#Component
public class ServiceInterceptor implements HandlerInterceptor {
private static final ApplicationLogger logger = ApplicationLogger.getInstance();
#Autowired
TokenInfoServiceImpl tokenInfoServiceImpl;
#Override
#CrossOrigin(origins = "*", maxAge = 3600)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestPath = request.getRequestURL().toString();
String authToken = request.getHeader("authToken");
String bearerToken = request.getHeader("Authorization");
String userId = request.getHeader("userId");
if (deviceId.equals("web")) {
if (bearerToken.startsWith("Bearer ")){
bearerToken = bearerToken.substring(7, bearerToken.length());
} else {
response.sendError(400, "Expected bearer prefix to Authorization header value.");
}
boolean isTokenValid = tokenInfoServiceImpl.validateToken(bearerToken);
return isTokenValid;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
My concern is since we have different services,every service has its interceptor class , so in every service will I have to create method validateToken(to validate the token when a request comes to that service) which is obviously not at all an ideal approach.
Is there a way that I could write validateToken method in one place and that could be accessed by all the services(like UserService,PaymentService,etc..) or rather one Interceptor could be used to intercept request for all the individual microservices instead of having separate interceptor for each service .
I know this can be done using API Gateway but right now our team want a quick solution to this .API Gateway will implement later.
If I understand your question & comments you can try below :
Create Configuration bean which implements WebMvcConfigurer
Use your ServiceInterceptor inside addInteceptor & mention endpoints or root context if all endpoints needed this config :
#Configuration
public class ConfigClass implements WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ServiceInterceptor ()).addPathPatterns("/contextroot/**");
}
}
Also you may directly use your ServiceInterceptor without annotating it with Component.

How to trigger a callback post fetching an entity in spring boot

#Service
#GetMapping
public Foo findByFooId(#RequestParam(name = "fid") String fooId) {
return fooService.findByFooId(fooId);
}
I would like to trigger and save who viewed Foo, using a different method in FooService.
Its like a PostConstruct callback for a successful response of findByFooId. How can this be achieved
One way is going to a custom HandlerInterceptor implementation.
Definition of the interceptor
public class FooViewerInterceptor extends HandlerInterceptorAdapter {
#Autowired
FooService fooService;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// if response succeeded ? http response code = 200 ?
// extract the "who" logic
// extract the fooId from request path
fooService.viewedBy(fooId, userId); // example...
}
}
Register the interceptor. Note the path pattern specified with the custom interceptor instance.. just an example.
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FooViewerInterceptor()).addPathPatterns("/foo/**");
}
}

Return 404 instead of 403 when #PostAuthorize fails

Let's say I have the following controller. (Assume that Order.customer is the customer the order belongs to and only they should be able to access it.)
#RestController
#RequestMapping("/orders")
public class OrderController {
#GetMapping
#PostAuthorize("returnObject.customer == authentication.principal")
public Order getOrderById(long id) {
/* Look up the order and return it */
}
}
After looking up the order, #PostAuthorize is used to make sure it belongs to the authenticated customer. If it is not, Spring responds with a 403 Forbidden.
Such an implementation has a problem: Clients can distinguish between orders that do not exist and orders they have no access to. Ideally, 404 should be returned in both cases.
While this could be solved by injecting the Authentication into the handler method and implementing custom logic there, is there any way to achieve this using #PostAuthorize or a similar, declarative API?
You can specify a custom AccessDeniedHandler in your Spring Security configuration.
In the following example, the handler will return a 404 Not Found on an access denied failure.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(accessDeniedHandler())
);
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
}
}
You could try a ControllerAdvice to catch and transform the AccessDeniedException, which PostAuthorize throws.
#RestControllerAdvice
public class ExceptionHandlerController {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(AccessDeniedException.class)
public String handleAccessDenied(AccessDeniedException e) {
return "nothing here"; // or a proper object
}
}

Spring MVC: how to get a handler method for a request

I am trying to implement some logic depending on annotation present on method with Spring #RequestMapping annotation.
So I have a HttpServletRequest instance in my method and I want to ask spring "give me a method, which will be invoked to handle this request", so I can use reflection API to ask if my annotation is present or not, so I can alter the processing.
Is there any easy way to get this information from Spring MVC?
I suppose you have a handler method like:
#SomeAnnotation
#RequestMapping(...)
public Something doHandle(...) { ... }
And you want to add some pre-processing logic for all handler methods that are annotated with #SomeAnnotation. Instead of your proposed approach, you can implement the HandlerInterceptor and put your pre-processing logic into the preHandle method:
public class SomeLogicInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
SomeAnnotation someAnnotation = handlerMethod.getMethodAnnotation(SomeAnnotation.class);
if (someAnnotation != null) {
// Put your logic here
}
}
return true; // return false if you want to abort the execution chain
}
}
Also don't forget to register your interceptor in your web configuration:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SomeLogicInterceptor());
}
}
Helped by #ali-dehgani's answer, I have a more flexible implementation that doesn't need to register an interceptor. You do need to pass the request object that is bound to be mapped to that method.
private boolean isHandlerMethodAnnotated(HttpServletRequest request ) {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
Map<String, HandlerMapping> handlerMappingMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(webApplicationContext, HandlerMapping.class, true, false);
try {
HandlerMapping handlerMapping = handlerMappingMap.get(RequestMappingHandlerMapping.class.getName());
HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
Object handler = handlerExecutionChain.getHandler();
if(handler instanceof HandlerMethod){
Annotation methodAnnotation = ((HandlerMethod) handler).getMethodAnnotation(MyAnnotation.class);
return methodAnnotation!=null;
}
} catch (Exception e) {
logger.warn(e);
}
return false;
}

Spring MVC - #ExceptionHandler based on Accept header

I have a HandlerInterceptorAdapter that intercepts all requests and performs user authorization checks. Very basically:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = ... // get user
checkIfAuthorized(user); // throws AuthorizationException
return true;
}
I then have an #ExceptionHandler for that AuthorizationException.
#ExceptionHandler(value = AuthorizationException.class)
public ResponseEntity<String> handleNotAuthorized(AuthorizationException e) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
return responseEntity;
}
This is fine if the (unauthorized) request accepts text/plain (and can be easily changed for json).
How can I make different #ExceptionHandlers for specific Accept headers?
#RequestMapping has produces(). Is there something similar for #ExceptionHandler?
I know this comes late but I've been looking up a solution to this, came across this question and found what I think to be a better solution. You can return "forward:/error" in your #ExceptionHandler (returning a String) to forward the request to a
#RequestMapping("/error")
ErrorController {...}
and use
#RequestMapping(produces = "text/html")
ModelAndView errorPage() {...}
on one method of that ErrorController,
#RequestMapping(produces = "application/json") // or no 'produces' attribute for a default
MyJsonObject errorJson() {...} on another.
I think this is a pretty neat way to do it, it's probably already out there but I didn't find it when trying to look it up.
So basically the #ExceptionHandler is the same for all, but forwards to a controller that can do the usual stuff
I think of two approaches:
Manually
public ResponseEntity<String> handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
if (/*read header accept from request and build appropiate response*/) {}
ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
return responseEntity;
Automatically
#ResponseBody
public SomeObject handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
/* Construct someObject and let Spring MessageConverters transform it to JSON or XML. I don't remember what happens in case of HTML (it should go to a view)*/
return someObject;
Don't forget to set the Response's Status code.
Not exactly the same use case, but the same requirement. I solve it with a custom HttpMessageConverter implementation.
#RestController
#RequestMapping("/foo")
public class MyResource {
#GetMapping(path = "/{id}", produces = "application/json")
public ResponseEntity<MyDto> get (#PathVariable(ID) long id)
throws IOException {
throw new MyCustomException();
}
#GetMapping(path = "/{id}/export", produces = "application/zip")
public ResponseEntity<byte[]> export (#PathVariable(ID) long id)
throws IOException {
throw new MyCustomException();
}
}
...
#ControllerAdvice
public class MyCustomExceptionHandler {
#ResponseBody
#ExceptionHandler
#ResponseStatus(BAD_REQUEST)
public JsonAPIErrorDocument handleException (MyCustomException e) {
return ....;
}
}
...
public class JsonAPIErrorDocumentToByteArrayMessageConverter extends AbstractHttpMessageConverter {
public ErrorDocumentToByteArrayMessageConverter () {
super(new MediaType("application", "zip"), MediaType.ALL);
}
#Override
protected boolean supports (Class clazz) {
return JsonAPIErrorDocument.class == clazz;
}
#Override
protected Object readInternal (Class clazz, HttpInputMessage inputMessage)
throws IOException,
HttpMessageNotReadableException {
return new byte[0];
}
#Override
protected void writeInternal (Object t, HttpOutputMessage outputMessage)
throws IOException,
HttpMessageNotWritableException {
}
}
...
#EnableWebMvc
#Configuration
#ComponentScan({ "com.foo" })
public class ApplicationConfig implements WebMvcConfigurer {
...
#Override
public void configureMessageConverters (List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new JsonAPIErrorDocumentToByteArrayMessageConverter());
}
...
}

Categories

Resources