Spring WebFlux集成Keycloak:JwtAuthenticationConverter未触发致403
Spring WebFlux+Security+Keycloak 合并客户端与资源服务器的403问题排查
核心问题根源
拆分部署时,客户端和资源服务器各自的认证流程独立触发;合并后,Spring Security的过滤器链优先级发生冲突:OAuth2客户端的登录过滤器会先处理请求,但资源服务器的JWT认证过滤器未被正确触发,导致自定义LibraryUserJwtAuthenticationConverter完全没执行,同时登录后的SecurityContext未被资源服务器的认证流程读取,最终返回403。
必须修正的配置点
1. 统一配置SecurityWebFilterChain,明确过滤器顺序
WebFlux环境下不能分开配置客户端和资源服务器的Security规则,必须在同一个SecurityWebFilterChain中整合,确保资源服务器的JWT认证逻辑能正确衔接客户端登录后的会话:
@Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, ReactiveOAuth2AuthorizedClientService authorizedClientService, JwtAuthenticationConverter jwtAuthenticationConverter) { http // 配置OAuth2客户端登录流程 .oauth2Login(oauth2 -> oauth2 .authorizedClientService(authorizedClientService) ) // 绑定自定义JWT转换器到资源服务器 .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter)) ) // 接口权限规则 .authorizeExchange(exchanges -> exchanges .pathMatchers("/login/**").permitAll() .pathMatchers("/users/**").hasAuthority("LIBRARY_USER") // 匹配转换器生成的权限 .anyExchange().authenticated() ) // WebFlux客户端场景下可禁用CSRF,避免拦截合法请求 .csrf(csrf -> csrf.disable()); return http.build(); }
2. 确保自定义转换器被正确注册并绑定
检查LibraryUserJwtAuthenticationConverter是否被Spring容器管理,且明确配置权限提取逻辑(以Keycloak的realm角色为例):
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { LibraryUserJwtAuthenticationConverter converter = new LibraryUserJwtAuthenticationConverter(); // 配置从JWT的realm_access.roles中提取权限 converter.setJwtGrantedAuthoritiesConverter(jwt -> { Map<String, Object> realmAccess = jwt.getClaim("realm_access"); if (realmAccess == null) return Collections.emptyList(); List<String> roles = (List<String>) realmAccess.get("roles"); return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); }); return converter; }
3. 修复SecurityContext存储与读取问题
WebFlux中客户端登录成功后,SecurityContext默认存在WebSession中,资源服务器需要正确读取该上下文:
- 若自定义了
ServerSecurityContextRepository,确保使用WebSessionServerSecurityContextRepository作为实现:
@Bean public ServerSecurityContextRepository securityContextRepository() { return new WebSessionServerSecurityContextRepository(); }
- 确认请求不会被跨域或其他过滤器拦截,导致WebSession中的SecurityContext无法传递。
4. Keycloak客户端配置校验
- 客户端
Access Type设为confidential,开启Direct Access Grants Enabled(如果需要密码模式登录)。 Valid Redirect URIs必须包含应用的登录回调地址,比如http://localhost:8080/login/oauth2/code/keycloak。
日志排查技巧
开启Spring Security DEBUG级别日志,重点查看:
logging.level.org.springframework.security=DEBUG
- 过滤器执行顺序:确认资源服务器的JWT过滤器是否在客户端登录过滤器之后执行。
JwtAuthenticationConverter相关日志:如果没有该类的调用记录,说明资源服务器的JWT认证逻辑未被触发。SecurityContextRepository日志:检查SecurityContext是否被正确存入WebSession,以及后续请求是否能读取到。
内容的提问来源于stack exchange,提问作者Saketh
相关产品推荐
相关产品推荐

