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

Spring 5中配置多Identity Provider(IDP)实现SAML认证

实现Spring SAML多IDP支持(基于URL动态路由)

针对你使用Spring 5 + spring-security-saml2-core:1.0.10.RELEASE从单IDP扩展多IDP的需求,以下是具体实现步骤和代码示例:


一、新增IDP元数据配置

将新增IDP的元数据XML文件放置到资源目录(比如src/main/resources/saml/idp2-metadata.xml),与原有IDP1的元数据文件共存。


二、修改SAML核心配置,注册多IDP

更新MetadataManager配置,添加多个IDP的元数据提供者:

@Configuration
public class SAMLConfig {

    @Bean
    public MetadataManager metadataManager() throws Exception {
        List<MetadataProvider> providers = new ArrayList<>();
        
        // 注册IDP1元数据
        ResourceBackedMetadataProvider idp1Metadata = new ResourceBackedMetadataProvider(
                new Timer(),
                new ClassPathResource("saml/idp1-metadata.xml")
        );
        idp1Metadata.setParserPool(parserPool());
        providers.add(idp1Metadata);
        
        // 注册IDP2元数据
        ResourceBackedMetadataProvider idp2Metadata = new ResourceBackedMetadataProvider(
                new Timer(),
                new ClassPathResource("saml/idp2-metadata.xml")
        );
        idp2Metadata.setParserPool(parserPool());
        providers.add(idp2Metadata);
        
        MetadataManager manager = new MetadataManager(providers);
        // 设置默认IDP(可选,未指定IDP时使用)
        manager.setDefaultIDP("idp1-entity-id"); // 替换为IDP1实际的实体ID
        return manager;
    }

    // 保持原有ParserPool配置不变
    @Bean
    public ParserPool parserPool() {
        BasicParserPool parserPool = new BasicParserPool();
        parserPool.setMaxPoolSize(10);
        return parserPool;
    }

    // 原有SAMLAuthenticationProvider、WebSSOProfile等Bean保持不变
}

三、自定义认证入口,实现URL路由到指定IDP

创建Controller处理/idp1和/idp2请求,手动触发对应IDP的SAML认证流程:

@Controller
public class IDPRedirectController {

    @Autowired
    private SAMLEntryPoint samlEntryPoint;
    
    @Autowired
    private MetadataManager metadataManager;

    @GetMapping("/idp1")
    public void redirectToIDP1(HttpServletRequest request, HttpServletResponse response) throws Exception {
        initiateSAMLAuthentication(request, response, "idp1-entity-id"); // 替换为IDP1实体ID
    }

    @GetMapping("/idp2")
    public void redirectToIDP2(HttpServletRequest request, HttpServletResponse response) throws Exception {
        initiateSAMLAuthentication(request, response, "idp2-entity-id"); // 替换为IDP2实体ID
    }

    private void initiateSAMLAuthentication(HttpServletRequest request, HttpServletResponse response, String idpEntityId) throws Exception {
        // 将目标IDP实体ID存入请求属性
        request.setAttribute(SAMLEntryPoint.SAML_ENTRY_POINT_IDP, idpEntityId);
        // 触发SAML认证流程
        samlEntryPoint.commence(request, response, new AuthenticationException("Trigger SAML auth for IDP: " + idpEntityId) {});
    }
}

四、扩展SAMLEntryPoint,支持动态IDP选择

修改SAMLEntryPoint配置,使其优先从请求属性中获取指定的IDP:

@Bean
public SAMLEntryPoint samlEntryPoint() {
    SAMLEntryPoint entryPoint = new SAMLEntryPoint() {
        @Override
        protected String getIDPEntityID(HttpServletRequest request, AuthenticationException exception) {
            // 优先读取请求属性中的IDP实体ID
            String idpEntityId = (String) request.getAttribute(SAML_ENTRY_POINT_IDP);
            if (idpEntityId != null && metadataManager.getIDPEntityIDs().contains(idpEntityId)) {
                return idpEntityId;
            }
            // 未指定则使用默认IDP
            return super.getIDPEntityID(request, exception);
        }
    };
    entryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
    return entryPoint;
}

@Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
    WebSSOProfileOptions options = new WebSSOProfileOptions();
    options.setIncludeScoping(false);
    return options;
}

五、调整Spring Security配置,开放新端点

确保/idp1、/idp2等端点允许匿名访问:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SAMLEntryPoint samlEntryPoint;
    
    @Autowired
    private SAMLAuthenticationProvider samlAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/idp1", "/idp2", "/saml/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .exceptionHandling()
                .authenticationEntryPoint(samlEntryPoint)
            .and()
            .logout()
                .logoutSuccessUrl("/")
            .and()
            .apply(samlSecurityConfigurer());
    }

    private SAMLConfigurer samlSecurityConfigurer() {
        return new SAMLConfigurer()
            .authenticationProvider(samlAuthenticationProvider)
            // 保留原有SAML相关配置(如ACS URL、密钥库等)
            ;
    }
}

关键注意事项

  • 确保每个IDP的实体ID唯一,且元数据文件配置正确
  • 验证所有IDP的证书已导入系统信任密钥库
  • 确认每个IDP的ACS回调URL(默认/saml/SSO)配置正确
  • 如需区分不同IDP的用户逻辑,可在SAMLAuthenticationProvider的authenticate方法中,通过SAMLCredential.getIDPEntityID()判断来源IDP并定制处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:11:09