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

Spring Boot同时集成SAML与表单认证时的无限重定向问题

Spring Boot 3.0.4 表单与SAML认证共存解决无限重定向问题

问题原因

同时配置formLogin和saml2Login的loginPage为同一个路径(/login)时,Spring Security的认证流程会出现逻辑冲突:未认证用户访问受保护资源会被重定向到/login,但该路径的请求会被认证过滤器反复拦截,触发循环重定向。

解决方案

核心是区分表单登录和SAML登录的入口路径,避免两者的认证逻辑冲突:

  • 移除SAML登录的loginPage配置,让SAML使用默认的认证入口路径
  • 在自定义登录页中为SAML登录按钮指定专属触发地址
  • 确保SAML相关路径允许未认证访问

修改后的安全配置代码

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
            .requestMatchers("/rs/test/**").permitAll()
            .requestMatchers("/login").permitAll()
            .requestMatchers("/saml2/**").permitAll() // 允许SAML相关路径未认证访问
            .requestMatchers("/webapp/**").authenticated()
            .requestMatchers("/public/**").permitAll()
            .requestMatchers("/rs/csrf/**").authenticated()
            .anyRequest().authenticated();

    http.cors();

    // 表单登录配置,保持自定义登录页
    http.formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/webapp/index.html", true);

    // 处理SAML2响应并从本地数据库加载角色
    OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
    authenticationProvider.setResponseAuthenticationConverter(responseToken -> {
        Saml2Authentication authentication = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter().convert(responseToken);
        Assertion assertion = responseToken.getResponse().getAssertions().get(0);
        String username = assertion.getSubject().getNameID().getValue();
        UserDetails userDetails = myUserDetailsService(dataSource).loadUserByUsername(username);
        authentication.setDetails(userDetails);
        if (userDetails.getAuthorities() != null && !userDetails.getAuthorities().isEmpty()) { // 修正判断条件,避免空指针
            return new Saml2Authentication((AuthenticatedPrincipal) authentication.getPrincipal(), responseToken.getResponse().toString(), userDetails.getAuthorities());
        } else {
            return authentication;
        }
    });

    // SAML登录配置,移除loginPage,使用默认认证入口
    http.saml2Login(saml2 -> saml2
            .authenticationManager(new ProviderManager(authenticationProvider))
    );

    return http.build();
}

自定义登录页调整

在/login页面中,分别配置表单登录和SAML登录的触发逻辑:

<!-- 表单登录区域 -->
<form action="/login" method="post">
    <!-- 若开启CSRF,需添加CSRF令牌 -->
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="text" name="username" placeholder="用户名" />
    <input type="password" name="password" placeholder="密码" />
    <button type="submit">表单登录</button>
</form>

<!-- SAML登录按钮 -->
<button onclick="window.location.href='/saml2/authenticate/idp'">DO SAML LOGIN</button>

注意:/saml2/authenticate/idp中的idp是你在配置文件中定义的SAML身份提供商(IDP)的注册ID,需与application.yml/application.properties中的配置一致:

spring:
  security:
    saml2:
      relyingparty:
        registration:
          idp: # 此处即为注册ID
            identityprovider:
              entity-id: https://your-idp-entity-id
              singlesignon:
                url: https://your-idp-sso-url
                binding: POST

额外说明

  • 修正了原代码中角色判断的逻辑错误(将||改为&&,避免空指针异常)
  • 确保SAML相关路径(/saml2/**)允许未认证访问,否则IDP的响应无法正常处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:07:45