Spring Authorization Server令牌端点404问题排查与配置咨询
问题描述
我使用org.springframework.security:spring-security-oauth2-authorization-server:1.2.2搭建简易授权服务器,参照官方文档配置后基础认证已生效,但调用自定义令牌端点http://localhost:8000/oauth2/v1/token(grant_type=client_credentials)时返回404错误,无法定位原因。以下是我的配置代码与调试日志,请求告知令牌URL的正确配置方式。
配置代码
@Configuration @EnableWebSecurity public class SecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { http .authorizeHttpRequests( ( authorize ) -> authorize .requestMatchers( "/oauth2/**" ).authenticated() .anyRequest().permitAll() ) .httpBasic( Customizer.withDefaults() ) .formLogin( Customizer.withDefaults() ) .cors( httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.disable()) .csrf( httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable()); return http.build(); } @Bean public AuthorizationServerSettings authorizationServerSettings( ){ return AuthorizationServerSettings.builder() .issuer( "http://localhost:8000" ) .tokenEndpoint( "/oauth2/v1/token" ) .tokenIntrospectionEndpoint( "/oauth2/v1/verify" ) .build(); } @Bean public OAuth2AuthorizationService authorizationService(){ return new InMemoryOAuth2AuthorizationService(); } @Bean public RegisteredClientRepository registeredClientRepository(){ RegisteredClient client = RegisteredClient.withId( UUID.randomUUID().toString() ) .clientId( "user" ) .clientSecret( "{noop}user" ) .clientAuthenticationMethod( ClientAuthenticationMethod.CLIENT_SECRET_BASIC ) .authorizationGrantType( AuthorizationGrantType.AUTHORIZATION_CODE ) .authorizationGrantType( AuthorizationGrantType.CLIENT_CREDENTIALS ) .authorizationGrantType( AuthorizationGrantType.REFRESH_TOKEN ) .redirectUri( "http://localhost:8000/redirect" ) .tokenSettings( TokenSettings.builder().accessTokenTimeToLive( Duration.ofDays( 1 ) ).build() ) .clientSettings( ClientSettings.builder() .build() ) .build(); return new InMemoryRegisteredClientRepository( client ); } @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); http.apply( authorizationServerConfigurer ); authorizationServerConfigurer .tokenEndpoint( Customizer.withDefaults() ) .tokenGenerator( new OAuth2AccessTokenGenerator() ) .authorizationServerSettings( authorizationServerSettings( ) ) .registeredClientRepository( registeredClientRepository() ) .authorizationService( authorizationService() ); // .setBuilder( http ); return http.build(); } }
调试日志
2024-02-27T16:07:12.942+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.security.web.FilterChainProxy : Securing POST /oauth2/v1/token 2024-02-27T16:07:13.147+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.s.a.dao.DaoAuthenticationProvider : Authenticated user 2024-02-27T16:07:13.148+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.s.w.a.www.BasicAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=user, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]] 2024-02-27T16:07:13.150+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.security.web.FilterChainProxy : Secured POST /oauth2/v1/token 2024-02-27T16:07:13.161+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.security.web.FilterChainProxy : Securing POST /error 2024-02-27T16:07:13.161+09:00 DEBUG 84973 --- [nio-8000-exec-8] o.s.security.web.FilterChainProxy : Secured POST /error
问题分析与解决方案
核心问题是两个SecurityFilterChain Bean的优先级和配置冲突,导致授权服务器的端点逻辑未被正确触发:
- 第一个
securityFilterChain拦截了/oauth2/**请求,完成认证后没有将请求转发到授权服务器的处理逻辑 - 授权服务器的
authorizationServerSecurityFilterChain未配置请求匹配规则,Spring无法识别哪些请求应该由它处理
修改后的完整配置代码
@Configuration @EnableWebSecurity public class SecurityConfiguration { // 普通Web安全过滤器链,优先级低于授权服务器链 @Bean @Order(2) public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .anyRequest().permitAll() ) .httpBasic(Customizer.withDefaults()) .formLogin(Customizer.withDefaults()) .cors(cors -> cors.disable()) .csrf(csrf -> csrf.disable()); return http.build(); } @Bean public AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder() .issuer("http://localhost:8000") .tokenEndpoint("/oauth2/v1/token") .tokenIntrospectionEndpoint("/oauth2/v1/verify") .build(); } @Bean public OAuth2AuthorizationService authorizationService() { return new InMemoryOAuth2AuthorizationService(); } @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("user") .clientSecret("{noop}user") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .redirectUri("http://localhost:8000/redirect") .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofDays(1)).build()) .clientSettings(ClientSettings.builder().build()) .build(); return new InMemoryRegisteredClientRepository(client); } // 授权服务器过滤器链,设置最高优先级 @Bean @Order(1) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); http .securityMatcher("/oauth2/**") // 指定该链处理授权相关请求 .apply(authorizationServerConfigurer); authorizationServerConfigurer .tokenEndpoint(Customizer.withDefaults()) .tokenGenerator(new OAuth2AccessTokenGenerator()) .authorizationServerSettings(authorizationServerSettings()) .registeredClientRepository(registeredClientRepository()) .authorizationService(authorizationService()); // 授权服务器端点的安全配置 http .cors(cors -> cors.disable()) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authorize -> authorize .anyRequest().authenticated() ); return http.build(); } }
关键修改点说明
- 过滤器链优先级:给两个
SecurityFilterChain添加@Order注解,让授权服务器的链(@Order(1))先执行,普通链(@Order(2))处理剩余请求 - 请求匹配规则:在授权服务器的过滤器链中添加
.securityMatcher("/oauth2/**"),明确该链负责所有授权端点的请求 - 规则去重:移除普通链中对
/oauth2/**的拦截,避免重复处理导致逻辑冲突 - 补充安全配置:给授权服务器链补充必要的cors、csrf禁用规则,确保端点正常接收请求
验证方式
修改后重启服务,调用POST http://localhost:8000/oauth2/v1/token,携带参数grant_type=client_credentials和Basic认证信息(用户名user,密码user),即可正常获取access_token。
内容的提问来源于stack exchange,提问作者seunggyu lee
相关产品推荐
相关产品推荐

