2

我一直在将 SAML 2.0 与 Spring Boot 2.5.6 一起使用,并使用 Okta 作为身份提供者。在大多数情况下,我已经能够创建一个 Web 应用程序并与 Okta 的身份提供程序集成。我面临的问题与角色有关。我在 Okta 中分配的组不会转换为 Spring Security 中的角色,并且不清楚这种转换应该如何在 Spring Security for SAML 中发生。在 SAML 断言中,组属性被命名为“组”,我可以在作为响应的一部分出现的断言 XML 中看到它。但是 Spring Security 不将“组”属性理解为角色。

在 Authentication 之后,SAML2Authentication 对象包含作为 ROLE_USER 的 Authorities。如何将 Assertion 中的传入组属性转换为 SAML2Authentication 中的适当权限?否则,我无法将应用程序 URL 配置为适当的授权角色。

请帮助或指出我的方向

4

1 回答 1

1

在 Spring Boot 中有多种方法可以连接到 SAML2 身份验证过程,但最常见且看似推荐的一种方法是自定义OpenSamlAuthenticationProviderSpring Security 用于身份验证的方法。

然而,该方法在最近的版本中发生了很多变化。下面的示例在 Kotlin 中,您必须自己将其翻译成 Java(如果这是您想要的)。另外,哪个 Spring Security 版本用在哪个 Spring Boot 版本中,你也得自己去摸索,但 2.5.6 版本肯定会把你放在下面的最后一种情况下。

弹簧安全 < 5.3.0

setAuthoritiesExtractor使得添加一个从断言中提取权限/角色的方法变得容易。该方法应该返回一个类型为 的对象Collection<GrantedAuthority>

.saml2Login()
    .authenticationManager(
        ProviderManager(
            OpenSamlAuthenticationProvider().apply {
                setAuthoritiesExtractor { a: Assertion? ->
                    // return a list of authorities based on the assertion
                }
            }
        )
    )

Spring Security >= 5.3.0 和 < 5.4.0

setAuthoritiesExtractor建议改为使用已弃用setResponseAuthenticationConverter。不幸的是,这比以前的方法更复杂。

.saml2Login()
    .authenticationManager(
        ProviderManager(
            OpenSamlAuthenticationProvider().apply {
                setResponseAuthenticationConverter {
                    val defaultAuth = OpenSamlAuthenticationProvider.createDefaultResponseAuthenticationConverter().convert(it);
                    if (defaultAuth !== null && defaultAuth.isAuthenticated) {
                        val authoritiesLists = it.response.assertions.map { ass -> 
                            // return a list of authorities based on the assertion     
                        }
                        val authoritiesList = authoritiesLists.flatten().toSet()        
                        Saml2Authentication(defaultAuth.principal as AuthenticatedPrincipal, defaultAuth.saml2Response, authoritiesList)
                    } 
                    else
                        defaultAuth
                }
            }
        )
    )

我不知道是否有更简单的方法可以做到这一点,但这里我们基本上使用默认行为,但按照我们想要的方式修改它。形式上,断言列表可能包含多个断言,因此我们通过使用多个列表来处理它,然后将这些列表展平并变成一个集合(如果您愿意,您可以只使用第一个断言,因为通常只有一个) .

弹簧安全 >= 5.4.0

这次发生的变化是,OpenSamlAuthenticationProvider由于它使用的 OpenSAML3 已达到其生命周期的尽头并已被 OpenSAML4 取代,因此它已被弃用。我们现在必须使用OpenSaml4AuthenticationProvider. 除此之外,配置部分保持不变

.saml2Login()
    .authenticationManager(
        ProviderManager(
            OpenSaml4AuthenticationProvider().apply {
                setResponseAuthenticationConverter {
                    val defaultAuth = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter().convert(it);
                    if (defaultAuth !== null && defaultAuth.isAuthenticated) {
                        val authoritiesLists = it.response.assertions.map { ass -> 
                            // return a list of authorities based on the assertion     
                        }
                        val authoritiesList = authoritiesLists.flatten().toSet()        
                        Saml2Authentication(defaultAuth.principal as AuthenticatedPrincipal, defaultAuth.saml2Response, authoritiesList)
                    } 
                    else
                        defaultAuth
                }
            }
        )
    )

尽管如此,这不会开箱即用,您必须对项目中的包语句进行一些修改,以强制 Spring Security 使用 OpenSAML4 而不是 OpenSAML3(由于向后兼容性,这是默认设置)。

下面是使用 Gradle 的样子

dependencies {
    constraints {
        implementation "org.opensaml:opensaml-core:4.1.1"
        implementation "org.opensaml:opensaml-saml-api:4.1.1"
        implementation "org.opensaml:opensaml-saml-impl:4.1.1"
    }
    ...
}

现在你准备好了!请记住, 的第一个参数Saml2Authentication是用户主体(继承自AuthenticatedPrincipal),也可以根据您自己的愿望和需要进行自定义。AuthenticatedPrincipal只需实现一个继承自响应身份验证转换器并向其添加逻辑的用户类,您就可以开始了。

链接

于 2021-11-14T11:12:14.057 回答