Spring OAuth2如何修改/oauth2/introspect端点无需client_id和client_secret
问题描述
原本实现的是自定义Spring OAuth2授权服务器的/oauth2/introspect端点来校验令牌,示例请求为:http://localhost:8080/oauth2/introspect?token=tokenValueHere&client_id=client&client_secret=secret,但需求调整为仅校验令牌,无需传入client_id和client_secret。
已尝试自定义getAuthenticationProvider方法,但不携带client_id和client_secret请求时,直接返回401 Unauthorized,且自定义方法中的打印语句Going to this method未输出,说明该方法被前置拦截,根本没执行。
相关代码如下:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .tokenIntrospectionEndpoint(a -> a.authenticationProvider(getAuthenticationProvider())); http.csrf().disable(); return http.build(); } private AuthenticationProvider getAuthenticationProvider() { System.out.println("Going to this method"); return new AuthenticationProvider() { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication.getPrincipal() == null || authentication.getCredentials() == null) { return authentication; } throw new BadCredentialsException("Invalid credentials"); } @Override public boolean supports(Class<?> authentication) { return OAuth2TokenIntrospectionAuthenticationToken.class.isAssignableFrom(authentication); } }; }
依赖配置:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-authorization-server</artifactId> <version>1.0.2</version> </dependency>
解决方案
出现401的核心原因是Spring Authorization Server默认要求/oauth2/introspect端点必须经过客户端认证,你的自定义AuthenticationProvider还没执行就被默认的客户端认证拦截了。要实现无客户端认证的令牌校验,需做以下调整:
1. 取消端点的客户端认证拦截
修改SecurityFilterChain配置,允许匿名访问/oauth2/introspect,同时覆盖默认的端点配置:
@Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); http.apply(authorizationServerConfigurer); // 配置令牌 introspect 端点的自定义逻辑 authorizationServerConfigurer.tokenIntrospectionEndpoint(endpoint -> endpoint.authenticationProvider(getIntrospectionAuthenticationProvider()) ); // 允许匿名访问introspect端点,绕过客户端认证 http.authorizeHttpRequests(auth -> auth .requestMatchers("/oauth2/introspect").permitAll() .anyRequest().authenticated() ); http.csrf().disable(); return http.build(); }
2. 实现真实令牌校验逻辑
原来的AuthenticationProvider逻辑未实际校验令牌有效性,需结合OAuth2AuthorizationService查询服务器存储的令牌状态:
@Autowired private OAuth2AuthorizationService authorizationService; private AuthenticationProvider getIntrospectionAuthenticationProvider() { return new AuthenticationProvider() { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { OAuth2TokenIntrospectionAuthenticationToken introspectionToken = (OAuth2TokenIntrospectionAuthenticationToken) authentication; String token = introspectionToken.getToken(); // 查询令牌是否存在且未过期 OAuth2Authorization authorization = authorizationService.findByToken( token, OAuth2TokenType.ACCESS_TOKEN ); Map<String, Object> attributes = new HashMap<>(); if (authorization == null || authorization.getAccessToken().isExpired()) { attributes.put("active", false); } else { attributes.put("active", true); attributes.put("sub", authorization.getPrincipalName()); // 可添加更多令牌属性,比如scope、exp等 } return new OAuth2TokenIntrospectionAuthenticationToken(attributes); } @Override public boolean supports(Class<?> authentication) { return OAuth2TokenIntrospectionAuthenticationToken.class.isAssignableFrom(authentication); } }; }
关键说明
- 必须通过
authorizeHttpRequests.permitAll()放开/oauth2/introspect的访问限制,否则默认的客户端认证过滤器会直接返回401。 - 利用
OAuth2AuthorizationService可以直接从授权服务器的存储中查询令牌状态,确保校验逻辑的准确性。
内容的提问来源于stack exchange,提问作者Sard
相关产品推荐
相关产品推荐

