Reputation: 147
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
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
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
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
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