如何在Spring WebFlux注解式控制器中按处理器方法拦截请求
当然可以实现!Spring WebFlux虽然是响应式架构,但完全能复刻你在Spring MVC里的权限校验逻辑,甚至更贴合响应式的设计。下面我就给你一步步拆解怎么适配到注解式控制器上:
1. 保留原有自定义注解和枚举
你的@Auth注解和AuthType枚举可以直接复用,不用做任何修改:
enum class AuthType { ALLOW, ADMIN } @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) annotation class Auth(val value: AuthType = AuthType.ALLOW)
2. 替换HandlerInterceptor为WebFilter
Spring WebFlux里没有HandlerInterceptorAdapter,替代方案是使用全局WebFilter——它能拦截所有请求,并且可以获取到注解式控制器对应的HandlerMethod,完美对应你原来的拦截器逻辑。
这里是适配后的响应式版本:
@Component class AccessFilter : WebFilter { // 假设你有用户服务用来解析令牌和查询用户 @Autowired private lateinit var userService: UserService override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> { // 获取当前请求匹配的处理器方法 val handlerMethod = exchange.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE) as? HandlerMethod ?: return chain.filter(exchange) // 非注解控制器请求,直接放行 // 优先取方法上的@Auth注解,没有则取类上的 val authAnnotation = handlerMethod.method.getAnnotation(Auth::class.java) ?: handlerMethod.beanType.getAnnotation(Auth::class.java) ?: return chain.filter(exchange) // 无注解,直接放行 // 允许访问的情况,直接放行 if (authAnnotation.value == AuthType.ALLOW) { return chain.filter(exchange) } // 响应式方式获取用户、校验权限 return getUserFromRequest(exchange.request) .flatMap { user -> if (isUserAuthorized(user, authAnnotation.value)) { chain.filter(exchange) } else { renderForbiddenResponse(exchange) } } // 捕获令牌错误、用户不存在等异常,统一返回403 .onErrorResume { renderForbiddenResponse(exchange) } } // 从请求头解析令牌并查询用户(响应式实现) private fun getUserFromRequest(request: ServerHttpRequest): Mono<User> { val token = request.headers.getFirst("Authorization")?.removePrefix("Bearer ") ?: return Mono.error(IllegalArgumentException("Missing authorization token")) return userService.findUserByToken(token) // 替换成你的实际令牌解析逻辑 } // 校验用户是否符合权限要求 private fun isUserAuthorized(user: User, authType: AuthType): Boolean { return when (authType) { AuthType.ADMIN -> user.roles.contains("ADMIN") else -> true } } // 返回403禁止访问的响应 private fun renderForbiddenResponse(exchange: ServerWebExchange): Mono<Void> { exchange.response.statusCode = HttpStatus.FORBIDDEN val errorBody = """{"code": 403, "message": "Access Denied"}""" return exchange.response.writeWith( Mono.just(exchange.response.bufferFactory().wrap(errorBody.toByteArray())) ) } }
3. 控制器用法完全不变
你的注解式控制器代码可以直接照搬,和Spring MVC里的写法完全一致:
@RestController class DemoController { @GetMapping("/foo") @Auth(AuthType.ALLOW) fun doesntNeedAuth(): Mono<String> { return Mono.just("Hello, no auth required!") } @GetMapping("/bar") @Auth(AuthType.ADMIN) fun adminMethod(): Mono<String> { return Mono.just("Hello, admin user!") } }
几个关键注意点
- 响应式编程要求所有异步操作都用
Mono/Flux包裹,不能像MVC那样同步返回布尔值,所以用户查询、错误处理都要遵循响应式规范 - 获取
HandlerMethod时,用HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE这个内置属性,是Spring WebFlux存储当前匹配处理器的标准键 - 如果你的权限逻辑更复杂,也可以考虑用
@PreAuthorize结合自定义方法安全表达式(需要开启@EnableReactiveMethodSecurity),但上面的WebFilter方式更贴合你原有代码的逻辑,迁移成本最低
内容的提问来源于stack exchange,提问作者NikichXP
相关产品推荐
相关产品推荐

