Spring Webflux自定义WebFilter导致开放端点均返回HTTP 200问题排查
问题
我正在开发Spring Boot Webflux应用,自定义了一个WebFilter用于基于请求中的Principal对象实现即时用户创建。该Filter在需要认证的端点上工作正常,但对于无需认证的端点(匹配/foo/**模式),无论端点是否存在,都会返回HTTP 200状态码,且端点似乎未被调用。
我的安全配置如下:
@Bean public SecurityWebFilterChain securityWebFilterChain( ServerHttpSecurity http) { return http.cors(cors -> cors.configurationSource(swx -> corsConfiguration())) .csrf(ServerHttpSecurity.CsrfSpec::disable) .securityMatcher(new NegatedServerWebExchangeMatcher( ServerWebExchangeMatchers.pathMatchers("/actuator/**"))) .authorizeExchange((authorize) -> authorize .pathMatchers("/foo/**") .permitAll() .anyExchange() .authenticated() ) .oauth2ResourceServer(ors -> ors.jwt(jwt -> {})) .build(); }
我的WebFilter代码如下:
@Component public class JITUserFilter implements WebFilter { private static final Logger logger = LoggerFactory.getLogger(JITUserFilter.class); private final UserService userService; public JITUserFilter(UserService userService) { this.userService = userService; } @Override public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { return serverWebExchange.getPrincipal().flatMap(p -> { Optional<String> email = Optional.empty(); if (p instanceof JwtAuthenticationToken) { email = Optional.of(((JwtAuthenticationToken)p).getToken().getClaimAsString("https://my-app.example.com/email")); } Optional<User> userOpt = userService.findUserByExternalId(p.getName()); if (userOpt.isEmpty()) { logger.info("No existing user found; creating new with externalId {}", p.getName()); User user = new User(); user.setExternalId(p.getName()); user.setEmail(email.orElse(null)); userService.createUser(user); } return webFilterChain.filter(serverWebExchange); }); } }
我在/foo/bar路径下有一个POST端点,预期被请求时会记录日志并返回模拟响应,但向该端点发送POST请求时仅得到200 OK响应,请问这是什么原因?
分析与解决方案
核心原因
你的JITUserFilter存在两个关键问题:
- 未处理Principal为空的情况:无需认证的
/foo/**端点请求中没有Principal对象,serverWebExchange.getPrincipal()会返回空Mono。你只通过flatMap处理了有Principal的场景,空Mono不会执行后续逻辑,也不会调用webFilterChain.filter(serverWebExchange),导致请求被“挂起”,Webflux最终默认返回200 OK,但实际端点从未被执行。 - 潜在阻塞风险:如果
userService.findUserByExternalId和userService.createUser是同步阻塞方法(比如基于JDBC的调用),会破坏Webflux的非阻塞特性,可能引发线程池阻塞问题。
修复步骤
- 补充Principal为空的分支处理:使用
switchIfEmpty确保无论是否存在Principal,请求都能传递到后续过滤器和端点:
@Override public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { return serverWebExchange.getPrincipal() .flatMap(p -> { Optional<String> email = Optional.empty(); if (p instanceof JwtAuthenticationToken) { email = Optional.of(((JwtAuthenticationToken)p).getToken().getClaimAsString("https://my-app.example.com/email")); } Optional<User> userOpt = userService.findUserByExternalId(p.getName()); if (userOpt.isEmpty()) { logger.info("No existing user found; creating new with externalId {}", p.getName()); User user = new User(); user.setExternalId(p.getName()); user.setEmail(email.orElse(null)); userService.createUser(user); } return webFilterChain.filter(serverWebExchange); }) // 处理无Principal的情况,继续请求链 .switchIfEmpty(webFilterChain.filter(serverWebExchange)); }
- 适配非阻塞调用(推荐):如果
userService的方法是阻塞式的,需要用Mono.fromCallable包装,符合Webflux的非阻塞模型:
@Override public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { return serverWebExchange.getPrincipal() .flatMap(p -> { Optional<String> email = Optional.empty(); if (p instanceof JwtAuthenticationToken) { email = Optional.of(((JwtAuthenticationToken)p).getToken().getClaimAsString("https://my-app.example.com/email")); } // 包装阻塞查询 return Mono.fromCallable(() -> userService.findUserByExternalId(p.getName())) .flatMap(userOpt -> { if (userOpt.isEmpty()) { // 包装阻塞创建操作 return Mono.fromCallable(() -> { User user = new User(); user.setExternalId(p.getName()); user.setEmail(email.orElse(null)); return userService.createUser(user); }); } return Mono.empty(); }) // 完成用户操作后继续请求链 .then(webFilterChain.filter(serverWebExchange)); }) .switchIfEmpty(webFilterChain.filter(serverWebExchange)); }
- 安全配置验证:当前安全配置中
permitAll()已正确放行/foo/**,securityMatcher仅排除了/actuator/**,对目标端点无影响,这部分无需修改。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

