NikichXP
NikichXP

Reputation: 147

How to intercept requests by handler method in Spring WebFlux

I've got following interceptor in Spring MVC that checks if user can access handler method:

class AccessInterceptor : HandlerInterceptorAdapter() {

override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any?): Boolean {
    val auth: Auth =
        (if (method.getAnnotation(Auth::class.java) != null) {
            method.getAnnotation(Auth::class.java)
        } else {
            method.declaringClass.getAnnotation(Auth::class.java)
        }) ?: return true
    if (auth.value == AuthType.ALLOW) {
        return true
    }

    val user = getUserFromRequest(request) // checks request for auth token
    // and checking auth for out user in future.
    return renderError(403, response)

In my Controller I do annotate methods, like this:

@GetMapping("/foo")
@Auth(AuthType.ALLOW)
fun doesntNeedAuth(...) { ... }

@GetMapping("/bar")
@Auth(AuthType.ADMIN)
fun adminMethod(...) { ... }

In case if user has wrong token or no permissions, error is being returned.
Is it possible to do this in Spring WebFlux with annotation-style controllers?

Upvotes: 5

Views: 4769

Answers (4)

Yuefeng Li
Yuefeng Li

Reputation: 463

My implementation, w/o using toFuture().get() which is potentially blocking.

@Component
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class QueryParameterValidationFilter implements WebFilter {

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    @NonNull
    @Override
    public Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) {
        return handlerMapping.getHandler(exchange)
            .doOnNext(handler -> validateParameters(handler, exchange))
            .then(chain.filter(exchange));
    }

    private void validateParameters(Object handler, ServerWebExchange exchange) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Set<String> expectedQueryParams = Arrays.stream(handlerMethod.getMethodParameters())
                .map(param -> param.getParameterAnnotation(RequestParam.class))
                .filter(Objects::nonNull)
                .map(RequestParam::name)
                .collect(Collectors.toSet());
            Set<String> actualQueryParams = exchange.getRequest().getQueryParams().keySet();
            actualQueryParams.forEach(actual -> {
                if (!expectedQueryParams.contains(actual)) {
                    throw new InvalidParameterException(ERR_MSG, actual);
                }
            });
        }
    }
}

Upvotes: 2

Redshift
Redshift

Reputation: 1

In newer versions of Spring the .toProcessor() call is deprecated. What worked for me is to use .toFuture().get() instead:

if(requestMappingHandlerMapping.getHandler(exchange).toFuture().get() instanceof HandlerMethod handlerMethod) { ... }

Unfortunately this requires handling of checked exceptions so the code will be a bit less readable but at least not deprecated anymore.

Upvotes: 0

Ivan
Ivan

Reputation: 2878

     @Component
     class AuditWebFilter(
          private val requestMapping: RequestMappingHandlerMapping
     ): WebFilter {

          override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
               // if not to call - then exchange.attributes will be empty
               // so little early initializate exchange.attributes by calling next line
               requestMapping.getHandler(exchange)

               val handlerFunction = exchange.attributes.get(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE) as HandlerMethod
               val annotationMethod = handlerFunction.method.getAnnotation(MyAnnotation::class.java)

               // annotationMethod  proccesing here
          }
     }

Upvotes: 1

Rozart
Rozart

Reputation: 1778

To solve that problem I would most probably use:

  • A Spring Reactive Web WebFilter from the WebHandler API to intercept the incoming request

  • The RequestMappingHandlerMapping to retrieve the method which handles the current request

@Autowired
RequestMappingHandlerMapping requestMappingHandlerMapping;

...
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    ...
    HandlerMethod handler = (HandlerMethod) requestMappingHandlerMapping.getHandler(exchange).toProcessor().peek();
    //your logic
}

Upvotes: 1

Related Questions