WebFlux项目如何在WebFilter中从OAuth2 JWT令牌提取accountId
问题根因
- 你写的校验逻辑没有接入响应式流:WebFlux是响应式编程模型,你定义的
ReactiveSecurityContextHolder.getContext()、serverWebExchange.getPrincipal()这一系列操作只是声明了处理逻辑,没有和后续的过滤器链执行逻辑拼接,也没有触发订阅,所以这部分代码实际根本不会运行。 - 过滤器执行顺序错误:如果你自定义的
WebFilter注册顺序早于Spring Security的认证过滤器链,执行校验逻辑时Spring Security还没有完成JWT令牌解析和认证上下文填充,自然拿不到用户信息。
实现方案
你不需要新增额外的SecurityWebFilterChain,只需要在现有安全配置中插入自定义校验过滤器,放在OAuth2资源服务器认证过滤器之后即可,正确代码如下:
1. 改造过滤器逻辑
public class AccountIdCheckFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // 此处根据你的实际URL结构提取路径中携带的accountId String clientAccountId = exchange.getRequest().getPath().value().split("/")[2]; return exchange.getPrincipal() .map(Principal::getName) // 校验JWT中解析的accountId和路径携带的是否一致 .filter(jwtAccountId -> jwtAccountId.equals(clientAccountId)) .switchIfEmpty(Mono.error(new SystemException("401 ERROR"))) // 校验通过后继续执行后续过滤器逻辑 .then(chain.filter(exchange)); } }
如果你的accountId是存储在JWT自定义Claim中而非Principal的name字段,可以用以下方式提取:
return exchange.getPrincipal() .cast(JwtAuthenticationToken.class) .map(auth -> auth.getTokenAttributes().get("accountId").toString()) .filter(jwtAccountId -> jwtAccountId.equals(clientAccountId)) .switchIfEmpty(Mono.error(new SystemException("401 ERROR"))) .then(chain.filter(exchange));
2. 调整安全配置,插入过滤器
修改你现有的SecurityWebFilterChain配置,将自定义过滤器放在OAuth2资源服务器认证过滤器之后,确保执行时认证信息已经填充完成:
@Bean SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .authorizeExchange() .pathMatchers("/api/**").authenticated() .anyExchange().authenticated() .and() .exceptionHandling() .accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST)) .and() .oauth2ResourceServer() .jwt() .jwtAuthenticationConverter(grantedAuthoritiesExtractor()); // 插入自定义校验过滤器,位置在OAuth2资源服务认证过滤器之后 http.addFilterAfter(new AccountIdCheckFilter(), SecurityWebFiltersOrder.OAUTH2_RESOURCE_SERVER); return http.build(); }
内容的提问来源于stack exchange,提问作者amir
相关产品推荐
相关产品推荐

