Spring MVC+Spring Security 6实现OAuth2登录页重定向问题
问题分析与解决方案
问题根源
- 手动跳转缺失OAuth2参数:直接使用
LoginUrlAuthenticationEntryPoint硬编码跳转地址时,Spring Security不会自动附加OAuth2授权流程必需的client_id、redirect_uri等参数,导致OAuth2服务器无法识别客户端。 - 默认跳转路径404:移除
exceptionHandling后,Spring Security默认跳转到/oauth2/authorization/{registrationId},该端点是OAuth2授权流程的触发入口,但如果过滤器链配置未覆盖该路径的处理逻辑,会导致404错误。
方案一:利用Spring Security原生OAuth2入口点处理跳转
使用OAuth2AuthorizationRequestRedirectingEntryPoint替代手动跳转,它会自动从ClientRegistrationRepository读取配置,构造包含所有必要参数的授权URL并完成重定向。
修改后的Security配置代码:
@Configuration @EnableWebSecurity public class SecurityConfiguration { private final CustomAuthorizationManager customAuthorizationManager; // 构造注入自定义授权管理器 public SecurityConfiguration(CustomAuthorizationManager customAuthorizationManager) { this.customAuthorizationManager = customAuthorizationManager; } @Bean @Order(1) public SecurityFilterChain filter1(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().access(customAuthorizationManager) ) .exceptionHandling(exceptions -> exceptions .defaultAuthenticationEntryPointFor( // 使用OAuth2原生入口点,指定客户端注册ID new OAuth2AuthorizationRequestRedirectingEntryPoint( clientRegistrationRepository, "my-combined-client" ), new MediaTypeRequestMatcher(MediaType.TEXT_HTML) ) ) .oauth2Login(oauth2 -> oauth2 .clientRegistrationRepository(clientRegistrationRepository) ) .oauth2Client(oauth2 -> oauth2 .clientRegistrationRepository(clientRegistrationRepository) ); return http.build(); } @Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(this.customClientRegistration()); } private ClientRegistration customClientRegistration() { return ClientRegistration .withRegistrationId("my-combined-client") .clientId("4d06125e-xxx") .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("https://xxx") .authorizationUri("https://xxx/op/v1/auth") .tokenUri("https://xxx/op/v1/token") .userInfoUri("https://xxx/op/v1/userinfo") .jwkSetUri("https://xxx/op/v1/keys") // 若有需要,添加scope配置 .scope("openid", "profile", "email") .build(); } }
方案二:自定义入口点手动构造授权URL
如果需要更灵活的参数控制,可以自定义AuthenticationEntryPoint,手动拼接包含所有必要参数的授权URL:
1. 自定义入口点实现
@Component public class CustomOAuth2EntryPoint implements AuthenticationEntryPoint { private final ClientRegistrationRepository clientRegistrationRepository; public CustomOAuth2EntryPoint(ClientRegistrationRepository clientRegistrationRepository) { this.clientRegistrationRepository = clientRegistrationRepository; } @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { ClientRegistration registration = clientRegistrationRepository.findByRegistrationId("my-combined-client"); if (registration == null) { throw new IllegalStateException("未找到ID为'my-combined-client'的客户端配置"); } // 构造完整授权URL,包含所有必需参数 UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(registration.getAuthorizationUri()) .queryParam("client_id", registration.getClientId()) .queryParam("redirect_uri", registration.getRedirectUri()) .queryParam("response_type", "code") .queryParam("scope", String.join(" ", registration.getScopes())); response.sendRedirect(uriBuilder.toUriString()); } }
2. 在Security配置中使用自定义入口点
@Bean @Order(1) public SecurityFilterChain filter1(HttpSecurity http, CustomOAuth2EntryPoint customEntryPoint) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().access(customAuthorizationManager) ) .exceptionHandling(exceptions -> exceptions .defaultAuthenticationEntryPointFor( customEntryPoint, new MediaTypeRequestMatcher(MediaType.TEXT_HTML) ) ) .oauth2Login(Customizer.withDefaults()) .oauth2Client(c -> c.clientRegistrationRepository(clientRegistrationRepository())); return http.build(); }
关键注意事项
- 确保
redirect_uri已在OAuth2服务器的客户端配置中被添加为允许的回调地址,否则授权后会回调失败。 - 如果客户端配置了
scope,必须在授权URL中包含该参数,否则OAuth2服务器可能返回权限错误。 - 验证
ClientRegistrationRepository已正确注入,且能读取到对应客户端的完整配置。
内容的提问来源于stack exchange,提问作者Korki Korkig
相关产品推荐
相关产品推荐

