Spring Security OAuth2令牌自省返回active:false问题排查
问题:JWT解码显示激活,但自省端点返回active:false
从Spring Security授权服务器获取新的JWT访问令牌后,解码令牌确认其处于激活状态,但调用Spring Security的/oauth2/introspect端点时,始终返回active:false。
相关日志:
logs:
2024-11-04T23:27:27.225+01:00 TRACE 26360 --- [nio-9090-exec-1] TokenIntrospectionAuthenticationProvider : Retrieved authorization with token
2024-11-04T23:27:27.225+01:00 TRACE 26360 --- [nio-9090-exec-1] TokenIntrospectionAuthenticationProvider : Did not introspect token since not active
期望自省端点返回active:true,当前授权服务器配置代码如下:
private static final String[] WHITELIST_URLS = { "/register", "/demo", "/authInfo", "/items" }; @Bean @Order(1) public SecurityFilterChain asSecurityFilterChain(HttpSecurity http) throws Exception { http .cors((cors) -> cors .configurationSource(corsConfigurationSource()) ); http.authorizeHttpRequests(authorize -> authorize .requestMatchers(WHITELIST_URLS).permitAll() ); OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); http.exceptionHandling( e -> e.authenticationEntryPoint( new LoginUrlAuthenticationEntryPoint("/login") ) ); http .oauth2ResourceServer((resourceServer) -> resourceServer .jwt(Customizer.withDefaults())); http .logout((logout) -> logout.logoutSuccessHandler(new CustomLogoutSuccessHandler())); return http .build(); } @Bean @Order(2) public SecurityFilterChain appSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.ignoringRequestMatchers("/logout", "/register", "/resetPassword", "/savePassword", "/changePassword")); http .cors((cors) -> cors .configurationSource(corsConfigurationSource()) ); http .httpBasic(withDefaults()) .formLogin(withDefaults()) .authorizeHttpRequests(authorize -> authorize .requestMatchers(WHITELIST_URLS).permitAll() .anyRequest().authenticated() ); return http.build(); } @Bean AuthorizationServerSettings authorizationServerSettings () { return AuthorizationServerSettings.builder().build(); }
问题根源分析
- 令牌状态存储缺失:Spring Security授权服务器需要存储颁发的令牌及状态,才能在自省端点验证令牌是否活跃。当前配置未添加令牌存储相关Bean,导致服务器无法跟踪令牌状态,直接返回
active:false。 - 过滤器链配置冲突:在
asSecurityFilterChain中,自定义的authorizeHttpRequests可能覆盖了授权服务器默认的端点权限配置,干扰了自省端点的正常逻辑处理。
修复方案
1. 添加令牌存储及JWT相关Bean
授权服务器需要依赖OAuth2AuthorizationService管理令牌状态,同时配置JWT编解码组件以支持令牌处理:
@Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); } @Bean public OAuth2TokenGenerator<?> tokenGenerator(JwtEncoder jwtEncoder) { JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); accessTokenGenerator.setJwtGenerator(jwtGenerator); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, refreshTokenGenerator); } @Bean public JwtEncoder jwtEncoder(KeyPair keyPair) { RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).build(); JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(rsaKey)); return new NimbusJwtEncoder(jwkSource); } @Bean public JwtDecoder jwtDecoder(KeyPair keyPair) { return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build(); } @Bean public KeyPair keyPair() { try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); return keyPairGenerator.generateKeyPair(); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException(ex); } }
2. 修正授权服务器过滤器链配置
移除可能覆盖默认端点权限的自定义规则,确保授权服务器默认配置生效:
@Bean @Order(1) public SecurityFilterChain asSecurityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) .exceptionHandling(e -> e.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))) .oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults())) .logout(logout -> logout.logoutSuccessHandler(new CustomLogoutSuccessHandler())); // 应用授权服务器默认安全配置,自动处理端点权限 OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); return http.build(); }
3. 确保客户端拥有自省权限
调用自省端点的客户端需注册introspect权限,示例配置如下:
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("your-client-id") .clientSecret("{noop}your-client-secret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .redirectUri("http://localhost:8080/login/oauth2/code/your-client") .scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .scope("introspect") // 添加自省权限 .build();
内容的提问来源于stack exchange,提问作者To O
相关产品推荐
相关产品推荐

