Spring WebFlux中ReactiveUserDetailsService被忽略问题求助
当使用OAuth2资源服务器的JWT模式时,Spring Security默认直接从JWT令牌中解析用户身份与权限信息,不会触发ReactiveUserDetailsService的调用逻辑——它默认认为JWT本身已经包含了完成认证所需的全部数据,无需再从数据库加载用户信息。
要让Spring Security在JWT认证流程中调用自定义的ReactiveUserDetailsService,需要修改安全配置,强制框架通过JWT中的用户标识(通常是sub字段)从数据库加载完整用户信息,具体步骤如下:
1. 配置JWT认证关联UserDetailsService
修改SecurityWebFilterChain中的oauth2ResourceServer配置,明确指定使用自定义的ReactiveUserDetailsService来加载用户:
@Bean SecurityWebFilterChain springSecurityFilterChain( ServerHttpSecurity http, ReactiveUserDetailsService userDetailsService // 注入自定义的UserDetailsService ) { // 原有CORS、csrf等配置保持不变 final CorsConfigurationSource configurationSource = serverWebExchange -> { final var cc = new CorsConfiguration(); cc.addAllowedOrigin("*"); cc.addAllowedMethod("*"); cc.addAllowedHeader("*"); return cc; }; Customizer<CorsSpec> corsCustomizer = (corsSpec) -> corsSpec.configurationSource(configurationSource); return http .httpBasic(HttpBasicSpec::disable) .cors(corsCustomizer) .csrf(CsrfSpec::disable) .formLogin(FormLoginSpec::disable) .anonymous(AnonymousSpec::disable) .logout(LogoutSpec::disable) .authorizeExchange((authorize) -> authorize .pathMatchers("/actuator/**").permitAll() .pathMatchers("/gicar/**").permitAll() .anyExchange().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt // 指定使用自定义的UserDetailsService加载用户 .userDetailsService(userDetailsService) ) ) .build(); }
2. 确保JWT与数据库用户标识匹配
验证你的JWT令牌中的sub字段(或你自定义的用户ID字段)与数据库中用户表的主键/唯一标识一致,这样ReactiveUserDetailsService才能根据JWT中的用户标识正确查询到对应的用户信息。
3. 可选:自定义JWT权限转换(如果需要)
如果你的JWT中的权限格式与Spring Security的GrantedAuthority格式不匹配(比如JWT里用roles而不是authorities),可以自定义JwtAuthenticationConverter来转换权限,同时结合UserDetailsService:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); // 假设JWT中权限存放在"roles"字段 grantedAuthoritiesConverter.setAuthoritiesClaimName("roles"); // 权限前缀设置为"ROLE_",匹配@PreAuthorize("hasRole('ADMIN')")中的格式 grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return converter; }
然后在oauth2ResourceServer的jwt配置中添加这个转换器:
.oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .userDetailsService(userDetailsService) .authenticationConverter(jwtAuthenticationConverter()) ) )
重新启动服务后,用之前的curl请求访问接口:
curl -s -X GET "http://$BACKEND/qdcf" -H "Authorization: Bearer $JWT_TOKEN"
此时Spring Security会先解析JWT获取用户标识,再调用你的GitUserDetailsService从数据库加载用户信息,之后再校验@PreAuthorize中的权限规则。
内容的提问来源于stack exchange,提问作者Jordi

