You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC+Spring Security 6实现OAuth2登录页重定向问题

问题分析与解决方案

问题根源

  1. 手动跳转缺失OAuth2参数:直接使用LoginUrlAuthenticationEntryPoint硬编码跳转地址时,Spring Security不会自动附加OAuth2授权流程必需的client_id、redirect_uri等参数,导致OAuth2服务器无法识别客户端。
  2. 默认跳转路径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();
}

关键注意事项

  1. 确保redirect_uri已在OAuth2服务器的客户端配置中被添加为允许的回调地址,否则授权后会回调失败。
  2. 如果客户端配置了scope,必须在授权URL中包含该参数,否则OAuth2服务器可能返回权限错误。
  3. 验证ClientRegistrationRepository已正确注入,且能读取到对应客户端的完整配置。

内容的提问来源于stack exchange,提问作者Korki Korkig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:53:19