Spring 安全 SAML2 动态选择 IDP 或他们的动态 URL

Spring Security SAML2 dynamic selection of IDPs or dynamic URLs for them

我们正在尝试在应用程序中配置多个身份提供者以支持不同类型的 SSO。 问题是对于未经身份验证的请求,应用程序不知道要重定向到哪个 IDP。 我们可以根据域名判断使用哪个IDP。那不是问题。 问题是更改过滤器重定向到特定 IDP 而不是第一个找到的 IDP 的方式。

我想知道 Spring 安全或其 SAML2 库是否有支持它的简单方法。

我可以以某种方式修改元数据以将其重定向到我自己的 URL(然后在那里有一些自定义代码)或使身份验证过滤器根据某些条件选择正确的 IDP。

更新

当前的 yaml 配置:

spring:
  security:
    saml2:
      relyingparty:
        registration:
          idpone:
            identityprovider:
              entity-id: https://idpone.com
              sso-url: https://idpone.com
              verification: 
                credentials:
                - certificate-location: "classpath:saml/idpone.crt"
          idptwo:
            identityprovider:
              entity-id: https://idptwo.com
              sso-url: https://idptwo.com
              verification: 
                credentials:
                - certificate-location: "classpath:saml/idptwo.crt"

从 Spring Security 5.2 开始,您可以通过 RelyingPartyRegistrationRepository.

设置多个 IDP

使用 Spring 引导时,每个看起来都像这样:

spring:
  security:
    saml2:
      relyingparty:
        registration:
          idpone:
            identityprovider:
              verification:
                credentials:
                  - certificate-location: "classpath:idpOne.crt"
              entity-id: https://idp.example.org
              sso-url: https://idp.example.org/SSOService.saml2
          idptwo:
            identityprovider:
              ...

然后,您可以通过导航至 http://localhost:8080/saml2/authenticate/idpOne.

idpOne 发起 AuthNRequest

按主机名

如果您想通过主机名执行此操作,则可以自定义 /login 页面以了解要重定向到哪个 /saml2/authenticate/{registrationId} 端点。

首先,您要告诉 Spring 安全您有一个自定义 /login 页面,因此它不会创建:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) {
        http
            .authorizeRequests(authz -> authz
                .mvcMatchers("/login").permitAll() // here
                .anyRequest().authenticated())
            .saml2Login(saml2 -> saml2.loginPage("/login")) // and here
    }

}

然后,您将定义它:

@Controller
public class LoginController {
    private final RelyingPartyRegistrationRepository relyingParties;

    // ... constructor

    @GetMapping("/login")
    public void login(HttpServletRequest request, HttpServletResponse response) {
        String registrationId = // ... derive from the host name
        RelyingPartyRegistration relyingParty = this.relyingParties
                .findByRegistrationId(registrationId);
        if (relyingParty == null) {
            response.setStatusCode(401);
        } else {
            response.sendRedirect("/saml2/authenticate/" + registrationId);
        }
    }
}

/login 端点中查找的原因是为了确保主机名中提供的 registrationId 是合法的。