Spring Authorization Server请求Token时重定向至登录页问题排查
OAuth2认证流程中
/oauth2/token接口返回302重定向问题排查 问题背景
我们有两个原基于CAS认证的Web应用,应用间原通过Basic Auth通信。出于安全考量,计划切换到OAuth2替代Basic Auth,同时弃用不符合需求的CAS。目前已实现:用户可登录管理系统并操作数据,应用2能正常跳转至应用1完成认证,应用1也能正确响应应用2,但应用2调用/oauth2/token接口时,应用1返回302重定向至/login而非Token。
相关配置信息
应用1(授权服务器)AuthorizationServerConfig配置
@Configuration public class AuthorizationServerConfig { private final SecurityApp app; public AuthorizationServerConfig(SecurityApp app) { this.app = app; } @Bean @Order(1) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); authorizationServerConfigurer .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint.consentPage("/oauth2/authorize")) .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 RequestMatcher endpointsMatcher = authorizationServerConfigurer .getEndpointsMatcher(); http .securityMatcher(endpointsMatcher) .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated() ) .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher)) .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) ) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) .apply(authorizationServerConfigurer); return http.build(); } @Bean @Order(2) public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ) // Form login handles the redirect to the login page from the // authorization server filter chain .formLogin(Customizer.withDefaults()); return http.build(); } @Bean UserDetailsService users() { return app::findUserByLogin; } // OVERWATCH @Bean public RegisteredClientRepository registeredClientRepository() { return new RegisteredClientRepository() { @Override public void save(RegisteredClient registeredClient) { throw new NotImplementedException(); } @Override public RegisteredClient findById(String id) { return app.findByClientId(id); } @Override public RegisteredClient findByClientId(String clientId) { return app.findByClientId(clientId); } }; } @Bean public JWKSource<SecurityContext> jwkSource() { RSAPublicKey publicKey = app.getPublicKey(); RSAPrivateKey privateKey = (RSAPrivateKey) app.getPrivateKey(); RSAKey rsaKey = new RSAKey.Builder(publicKey) .privateKey(privateKey) .keyID(UUID.randomUUID().toString()) .build(); JWKSet jwkSet = new JWKSet(rsaKey); return new ImmutableJWKSet<>(jwkSet); } @Bean public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) { return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); } @Bean public AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().build(); } // region Password Authenticator @Bean public PasswordEncoder passwordEncoder() { return app.passwordEncoder(); } // endregion }
应用2安全配置
@Configuration @EnableWebSecurity public class AceSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .antMatcher("/**") .authorizeRequests() .antMatchers("/oauth/authorize**", "/login**", "/error**") .permitAll() .and() .authorizeRequests() .anyRequest().authenticated() .and() .oauth2Login( oauth2Login -> oauth2Login.defaultSuccessUrl("/index.html") ); } }
应用2application.properties配置
spring.security.oauth2.client.registration.<provider-name>.client-id=${app.uuid} spring.security.oauth2.client.registration.<provider-name>.client-secret=${app.secret} spring.security.oauth2.client.registration.<provider-name>.scope=openid spring.security.oauth2.client.registration.<provider-name>.redirect-uri=http://127.0.0.1:8088/login/oauth2/code/<provider-name> spring.security.oauth2.client.registration.<provider-name>.client-name=${app.name} spring.security.oauth2.client.registration.<provider-name>.provider=${provider.name} spring.security.oauth2.client.registration.<provider-name>.client-authentication-method=code spring.security.oauth2.client.registration.<provider-name>.authorization-grant-type=authorization_code spring.security.oauth2.client.provider.<provider-name>.authorization-uri=http://localhost:8086/oauth2/authorize spring.security.oauth2.client.provider.<provider-name>.token-uri=http://localhost:8086/oauth2/token spring.security.oauth2.client.provider.<provider-name>.user-info-uri=http://localhost:8086/oauth2/userinfo?schema=openid spring.security.oauth2.client.provider.<provider-name>.user-name-attribute=name spring.security.oauth2.client.provider.<provider-name>.user-info-authentication-method=header spring.security.oauth2.client.provider.<provider-name>.jwk-set-uri=http://localhost:8086/jwks
触发重定向的请求
POST http://localhost:8086/oauth2/token Body: grant_type=authorization_code, code=Zls0ppjnS_RXyMVPB8fg_eQQgoiUAxRguOMsdyVYQpgd8eDkUDzgz813L0ybovTL7sNj0TDRUHibPfek9NzwULND1mty5WPW2DOtQjTAaEROL3qP7RvyTWXTEzzYe-o, redirect_uri=http://127.0.0.1:8088/login/oauth2/code/<provider-name>, client_id=<app.uuid> Header: Accept:"application/json;charset=UTF-8", Content-Type:"application/x-www-form-urlencoded;charset=UTF-8"
遗漏配置与修复方案
1. 客户端认证方式配置错误
应用2的application.properties中,client-authentication-method设置为code完全错误。授权码模式下,客户端调用/oauth2/token接口时,应使用client_secret_basic或client_secret_post的认证方式。
修改配置:
spring.security.oauth2.client.registration.<provider-name>.client-authentication-method=client_secret_basic
2. 客户端请求缺失凭证
当前触发重定向的请求仅传递了client_id,未携带client_secret,也未通过Basic Auth头传递客户端凭证。需补充:
- 方式一:在请求头添加
Authorization: Basic <base64编码的client_id:client_secret> - 方式二:若使用
client_secret_post认证方式,在请求体中添加client_secret=<app.secret>
3. 授权服务器客户端配置校验
检查应用1中app.findByClientId(clientId)返回的RegisteredClient实例,需确保:
clientAuthenticationMethod()设置为ClientAuthenticationMethod.CLIENT_SECRET_BASIC(或对应配置的认证方式)authorizationGrantTypes()包含AuthorizationGrantType.AUTHORIZATION_CODEclientSecret与应用2配置的<app.secret>一致,且已通过正确的PasswordEncoder编码
4. 授权服务器安全链异常处理优化
当前授权服务器对所有端点使用LoginUrlAuthenticationEntryPoint,会将未认证请求重定向到登录页,但/oauth2/token属于客户端认证端点,应返回401而非重定向。修改安全链的异常处理逻辑:
.exceptionHandling(exceptions -> exceptions .defaultAuthenticationEntryPointFor( new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), new AntPathRequestMatcher("/oauth2/token") ) .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) )
内容的提问来源于stack exchange,提问作者a1nez
相关产品推荐
相关产品推荐

