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

如何在Spring Security应用中实现SSO自动IDP会话校验?

问题描述

我通过以下代码实现了基于OAuth2和第三方IDP的登录功能:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests -> authorizeRequests
//                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .antMatchers("/", "/node_modules/**").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login()

    }
}

现在我希望应用能在每次请求时校验IDP的会话并实现SSO自动登录,于是尝试了如下配置:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests -> authorizeRequests
//                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .antMatchers("/", "/node_modules/**").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login()
                .userInfoEndpoint()
                .userService(oauth2UserService());
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
        return userRequest -> {
            OAuth2User oAuth2User = delegate.loadUser(userRequest);
            // You can customize the OAuth2User object here, if needed
            return oAuth2User;
        };
    }
}

预期打开页面时会自动重定向到IDP完成登录,但实际没有任何反应,该如何解决?

解决方案

1. 理解默认行为的限制

你的配置中根路径/被设置为permitAll(),访问该路径时Spring Security不会触发认证流程;同时默认OAuth2登录是被动触发的——只有访问受保护资源时才会跳转到IDP。要实现主动校验IDP会话并自动登录,需要调整配置触发主动SSO检查,同时依赖IDP支持静默登录(通过OpenID Connect的prompt=none参数实现)。

2. 方案一:修改授权规则+配置自动跳转

如果希望访问根路径时也触发SSO校验,可修改授权规则并配置OAuth2自动跳转逻辑:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests -> authorizeRequests
                        .antMatchers("/node_modules/**").permitAll()
                        // 根路径也需认证,触发SSO检查
                        .anyRequest().authenticated())
                .oauth2Login(oauth2 -> oauth2
                        // 登录成功后默认跳转回原路径
                        .defaultSuccessUrl("/", true)
                        // 添加prompt=none参数,让IDP静默检查会话
                        .authorizationEndpoint(auth -> auth
                                .authorizationRequestResolver(requestResolver())));
    }

    private OAuth2AuthorizationRequestResolver requestResolver() {
        DefaultOAuth2AuthorizationRequestResolver resolver =
                new DefaultOAuth2AuthorizationRequestResolver(
                        clientRegistrationRepository(), "/oauth2/authorization");
        resolver.setAuthorizationRequestCustomizer(customizer ->
                customizer.additionalParameters(params -> params.put("prompt", "none")));
        return resolver;
    }

    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;
}

3. 方案二:添加自定义过滤器实现主动校验

如果需要保留根路径的匿名访问,但仍要主动检查IDP会话(用户已在IDP登录时自动登录应用),可添加自定义过滤器:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests -> authorizeRequests
                        .antMatchers("/", "/node_modules/**").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login()
                .userInfoEndpoint()
                .userService(oauth2UserService())
                .and()
                .addFilterBefore(ssoInitiationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    private Filter ssoInitiationFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
                Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                // 未认证且非登录回调路径时,触发静默SSO检查
                if (auth == null || !(auth instanceof OAuth2AuthenticationToken)) {
                    String requestUri = request.getRequestURI();
                    if (!requestUri.startsWith("/oauth2/callback") && !requestUri.startsWith("/login")) {
                        // 替换your-idp-registration-id为配置文件中的IDP注册ID
                        response.sendRedirect("/oauth2/authorization/your-idp-registration-id?prompt=none");
                        return;
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
        return userRequest -> {
            OAuth2User oAuth2User = delegate.loadUser(userRequest);
            // 自定义逻辑保留
            return oAuth2User;
        };
    }
}

4. 关键配置检查

  • 确认IDP支持prompt=none参数(多数OpenID Connect标准IDP都支持),该参数会让IDP静默检查用户会话,无需手动输入账号密码。
  • 检查配置文件中的OAuth2客户端信息:client-id、client-secret、authorization-uri、token-uri、redirect-uri必须与IDP后台配置一致。
  • 确保IDP后台已将应用的回调地址加入允许列表,否则会导致认证失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:35:16