You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 15:18:07