SAML 2.0 迁移
当 <saml2:LogoutRequest> 验证失败时,期望收到 <saml2:LogoutResponse>
SAML身份提供者期望服务提供者在无法处理 <saml2:LogoutRequest> 时返回一个错误 <saml2:LogoutResponse>。
Spring Security 的旧版本在某些情况下会返回 401 状态码,这会中断来自各个依赖方的注销请求与响应链。
在 Spring Security 7 中,此行为已得到修复,您无需进行任何操作。
不过,如果这给您带来了麻烦,您可以通过发布一个 Saml2LogoutRequestResolver 来恢复旧的行为,该解析器在需要错误 <saml2:LogoutRequest> 时返回 null。您可以创建一个像这样的委托:
- Java
- Kotlin
@Bean
Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {
OpenSaml5LogoutResponseResolver delegate = new OpenSaml5LogoutResponseResolver(registrations);
return new Saml2LogoutResponseResolver() {
@Override
public void resolve(HttpServletRequest request, Authentication authentication) {
delegate.resolve(request, authentication);
}
@Override
public void resolve(HttpServletRequest request, Authentication authentication, Saml2AuthenticationException error) {
return null;
}
};
}
@Bean
fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver {
val delegate = OpenSaml5LogoutResponseResolver(registrations)
return object : Saml2LogoutResponseResolver() {
override fun resolve(request: HttpServletRequest?, authentication: Authentication?) {
delegate.resolve(request, authentication)
}
override fun resolve(request: HttpServletRequest?, authentication: Authentication?, error: Saml2AuthenticationException?) {
return null
}
}
}
优先使用 Saml2ResponseAuthenticationAccessor 而非 Saml2AuthenticatedPrincipal
Spring Security 7 将 <saml2:Assertion> 的详细信息与主体(principal)分离。这使得 Spring Security 能够获取所需的断言细节以执行单点登出。
这标志着 Saml2AuthenticatedPrincipal 已被弃用。您不再需要实现它来使用 Saml2Authentication。
相反,该凭证实现了Saml2ResponseAssertionAccessor接口,Spring Security 7 在根据认证确定相应操作时会优先使用此接口。
使用默认设置时,此更改会自动为您完成。
如果在升级过程中遇到问题,您可以发布一个自定义的 ResponseAuthenticationConverter,使其返回 Saml2Authentication 而不是 Saml2AssertionAuthentication,具体操作如下:
- Java
- Kotlin
@Bean
OpenSaml5AuthenticationProvider authenticationProvider() {
OpenSaml5AuthenticationProvider authenticationProvider =
new OpenSaml5AuthenticationProvider();
ResponseAuthenticationConverter defaults = new ResponseAuthenticationConverter();
authenticationProvider.setResponseAuthenticationConverter(
defaults.andThen((authentication) -> new Saml2Authentication(
authentication.getPrincipal(),
authentication.getSaml2Response(),
authentication.getAuthorities())));
return authenticationProvider;
}
@Bean
fun authenticationProvider(): OpenSaml5AuthenticationProvider {
val authenticationProvider = OpenSaml5AuthenticationProvider()
val defaults = ResponseAuthenticationConverter()
authenticationProvider.setResponseAuthenticationConverter(
defaults.andThen { authentication ->
Saml2Authentication(authentication.getPrincipal(),
authentication.getSaml2Response(),
authentication.getAuthorities())
})
return authenticationProvider
}
如果你正在自行构建 Saml2Authentication 实例,请考虑改用 Saml2AssertionAuthentication 以获得与当前默认设置相同的优势。
请勿使用 Saml2AuthenticationTokenConverter 处理 <saml2:Response> GET 请求
Spring Security 不支持通过 GET 请求处理 <saml2:Response> 负载,因为 SAML 2.0 规范不支持此方式。
为了更好地遵循这一规范,从 Spring Security 8 开始,Saml2AuthenticationTokenConverter 和 OpenSaml5AuthenticationTokenConverter 默认将不再处理 GET 请求。为了提前做好准备,现已提供 shouldConvertGetRequests 属性。要使用它,请按如下方式发布您自己的转换器:
- Java
- Kotlin
@Bean
OpenSaml5AuthenticationTokenConverter authenticationConverter(RelyingPartyRegistrationRepository registrations) {
OpenSaml5AuthenticationTokenConverter authenticationConverter = new OpenSaml5AuthenticationTokenConverter(registrations);
authenticationConverter.setShouldConvertGetRequests(false);
return authenticationConverter;
}
@Bean
fun authenticationConverter(val registrations: RelyingPartyRegistrationRepository): Saml2AuthenticationTokenConverter {
val authenticationConverter = Saml2AuthenticationTokenConverter(registrations)
authenticationConverter.setShouldConvertGetRequests(false)
return authenticationConverter
}
如果你必须继续使用 Saml2AuthenticationTokenConverter 或 OpenSaml5AuthenticationTokenConverter 来处理 GET 请求,可以将 setShouldConvertGetRequests 设置为 true。