在Spring Boot 4(Spring Security 6)中,我们将从我们自己的SAML实现转移到Spring Security的SAML实现。发送Saml2LogoutRequest
会导致SAML2-Server(在我的测试场景中是一个simpleSamlPhp示例)中出现会话丢失错误,并且不会执行注销。重新加载页面会在后台创建一个新会话,因为用户只是从SpringBoot应用程序注销。
我在调试问题时发现了以下内容:
POST到logout-Endpoint导致LogoutFilter.doFilter()
被调用,用户被注销系统,如日志所示:
2023-02-21T16:19:24.369+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy : Securing POST /sso/saml/logout
2023-02-21T16:19:24.371+01:00 DEBUG 897328 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=AiToolSaml2Authentication [Principal=org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal@669175a9, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=73598BB643535D6256B71F961AFF7266], Granted Authorities=[ROLE_CONSUMER]]]
2023-02-21T16:20:09.065+01:00 WARN 897328 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=48s315ms980µs301ns).
2023-02-21T16:20:10.414+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.s.w.a.logout.LogoutFilter : Logging out [AiToolSaml2Authentication [Principal=org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal@669175a9, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=73598BB643535D6256B71F961AFF7266], Granted Authorities=[ROLE_CONSUMER]]]
2023-02-21T16:20:16.845+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.s.w.a.l.SecurityContextLogoutHandler : Invalidated session 348DCC90A54531D2C855906B14A6F8E4
然后调用Saml2RelyingPartyInitiatedLogoutSuccessHandler.onLogoutSuccess()
,生成一个Saml2LogoutRequest
,这个请求通过doRedirect()
重定向到SAML2服务器。
我可以在SAML服务器的端点'SingleLogoutService.php'中看到请求,之后调用了resumelogout.php ',它无法声明状态信息丢失,如here所述。
就我所见,请求本身看起来不错,我还检查了SAML cookie中设置的SameSite = None和Secure = 1。
在我们自己的实现中,我们曾经有一个GET请求来注销重定向到SAML服务器,使用相同的SAML配置可以很好地工作。Saml2LogoutRequest
需要设置Issuer,NameID,ID和IssueInstant,唯一需要覆盖的是NameID,我通过Saml2LogoutRequestResolver
完成了这一操作。
我不确定到底是什么失败了,我也不确定是否是SpringBoot-App中的预注销导致了这个问题。
有什么想法吗?
当前的测试配置如下所示(本地主机上只有http,我们的测试环境使用https;但误差是相同):
spring:
security:
saml2:
relyingparty:
registration:
saml:
assertingparty:
entity-id: "http://localhost:8000/saml2/idp/metadata.php"
metadata-uri: "http://localhost:8000/saml2/idp/metadata.php"
singlesignon:
url: "http://localhost:8000/saml2/idp/SSOService.php"
entity-id: "http://localhost:8080"
singlelogout:
url: "http://localhost:8000/saml2/idp/SingleLogoutService.php"
signing:
credentials:
- certificate-location: "classpath:saml.crt"
private-key-location: "classpath:saml.key"
安全配置如下所示:
@Bean
fun filterChain(http: HttpSecurity, registrations: RelyingPartyRegistrationRepository): SecurityFilterChain {
...
// saml (sso) login
http.saml2Login { saml2LoginConfigurer ->
saml2LoginConfigurer
.authenticationRequestUri("/sso/{registrationId}/login")
.loginProcessingUrl(samlResponseUrl.replace("{baseUrl}", ""))
.authenticationManager(ProviderManager(samlAuthenticationProvider()))
.defaultSuccessUrl(samlRedirectUrl)
.failureUrl(samlRedirectUrlNewUser)
}
// saml (sso) logout
http.saml2Logout { saml2LogoutConfigurer ->
saml2LogoutConfigurer
.logoutUrl("/sso/saml/logout")
.logoutRequest { request ->
request.logoutRequestResolver(
logoutRequestResolver(DefaultRelyingPartyRegistrationResolver(registrations))
)
}
}
...
}
@Bean
fun samlAuthenticationProvider(): OpenSaml4AuthenticationProvider =
// provider configuration
OpenSaml4AuthenticationProvider().apply {
setResponseAuthenticationConverter { responseToken: OpenSaml4AuthenticationProvider.ResponseToken ->
val authentication = OpenSaml4AuthenticationProvider
.createDefaultResponseAuthenticationConverter()
.convert(responseToken)
val principal = (authentication!!.principal as DefaultSaml2AuthenticatedPrincipal)
val email = principal.attributes[samlEmailAttributName]
check(email != null) { "Configured email attribute not found in SAML response." }
check(email.size == 1) { "Configured email attribute is not unique." }
check(email[0] is String) { "Configured email attribute value is not a string." }
val user = (authenticationService.loadUserByUsername(email[0] as String)
AiToolSaml2Authentication(
user,
principal,
authentication.credentials.toString(),
user.authorities
)
}
}
@Bean
fun logoutRequestResolver(
registrationResolver: RelyingPartyRegistrationResolver
): Saml2LogoutRequestResolver =
OpenSaml4LogoutRequestResolver(registrationResolver).apply {
setParametersConsumer { parameters ->
with(parameters.logoutRequest) {
nameID.value = parameters.authentication.getUserInternal().user.email
}
}
}
1条答案
按热度按时间2mbi3lxu1#
这整个事情原来是一个CORS问题。正确设置访问控制头并允许在客户端中CORS提交凭据可以解决这个问题。