SP 超时时 SLO 失败
SLO fails when SP has timed out
使用 Spring 安全 SAML 1.0.10,当 SAML 会话在 SP 上超时,但在 IdP 上仍处于活动状态时,SLO 尝试失败。
如何重现
- 将会话超时设置为一分钟 (server.servlet.session = 1m)
- 登录您的 SP
- 在不同的辅助 SP 上登录 - 它必须使用 SSO
- 等待一分钟让会话超时
- 锁定辅助 SP
这会导致 Spring 中出现 ClassCastException,因为 SecurityContextHolder.getContext().getAuthentication()
包含 AnonymousAuthenticationToken
,但不包含凭据。它还会破坏过滤器链,永远不会将 LogoutResponse 发送到 IdP,并且会在浏览器中显示错误页面。
java.lang.ClassCastException: class java.lang.String cannot be cast to class org.springframework.security.saml.SAMLCredential (java.lang.String is in module java.base of loader 'bootstrap'; org.springframework.security.saml.SAMLCredential is in unnamed module of loader 'app')
at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:172)
这是有意为之,是错误,还是我配置有误?
如果是有意为之或存在错误,是否有解决方法?
错误发生在 SAMLLogoutProcessingFilter.processLogout()
,其中以下代码导致 ClassCastException。如果用户会话超时 auth.getCredentials()
returns 一个空字符串。
SAMLCredential credential = null;
if (auth != null) {
credential = (SAMLCredential)auth.getCredentials();
}
我发现的唯一解决方法是创建一个 LogoutFilter 来处理这个问题。
我创建了一个扩展 SAMLLogoutProcessingFilter 的新 LogoutFilter。对于 LogoutRequest 和 AnonymousAuthenticationToken,我创建了一个 LogoutResponse。在任何其他情况下,我转发到 SAMLLogoutProcessingFilter 或从 SAMLLogoutProcessingFilter 调用方法的副本,我已经删除了失败的部分。
private void overwrittenLogout(HttpServletRequest request, HttpServletResponse response, SAMLMessageContext context)
throws ServletException, SAMLException, MetadataProviderException, MessageEncodingException
{
var auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof AnonymousAuthenticationToken) {
logoutProfile.sendLogoutResponse(context, StatusCode.SUCCESS_URI, null);
} else if (auth != null) {
followNormalProcedure(request, response, auth, context);
}
}
使用 Spring 安全 SAML 1.0.10,当 SAML 会话在 SP 上超时,但在 IdP 上仍处于活动状态时,SLO 尝试失败。
如何重现
- 将会话超时设置为一分钟 (server.servlet.session = 1m)
- 登录您的 SP
- 在不同的辅助 SP 上登录 - 它必须使用 SSO
- 等待一分钟让会话超时
- 锁定辅助 SP
这会导致 Spring 中出现 ClassCastException,因为 SecurityContextHolder.getContext().getAuthentication()
包含 AnonymousAuthenticationToken
,但不包含凭据。它还会破坏过滤器链,永远不会将 LogoutResponse 发送到 IdP,并且会在浏览器中显示错误页面。
java.lang.ClassCastException: class java.lang.String cannot be cast to class org.springframework.security.saml.SAMLCredential (java.lang.String is in module java.base of loader 'bootstrap'; org.springframework.security.saml.SAMLCredential is in unnamed module of loader 'app')
at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:172)
这是有意为之,是错误,还是我配置有误? 如果是有意为之或存在错误,是否有解决方法?
错误发生在 SAMLLogoutProcessingFilter.processLogout()
,其中以下代码导致 ClassCastException。如果用户会话超时 auth.getCredentials()
returns 一个空字符串。
SAMLCredential credential = null;
if (auth != null) {
credential = (SAMLCredential)auth.getCredentials();
}
我发现的唯一解决方法是创建一个 LogoutFilter 来处理这个问题。
我创建了一个扩展 SAMLLogoutProcessingFilter 的新 LogoutFilter。对于 LogoutRequest 和 AnonymousAuthenticationToken,我创建了一个 LogoutResponse。在任何其他情况下,我转发到 SAMLLogoutProcessingFilter 或从 SAMLLogoutProcessingFilter 调用方法的副本,我已经删除了失败的部分。
private void overwrittenLogout(HttpServletRequest request, HttpServletResponse response, SAMLMessageContext context)
throws ServletException, SAMLException, MetadataProviderException, MessageEncodingException
{
var auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof AnonymousAuthenticationToken) {
logoutProfile.sendLogoutResponse(context, StatusCode.SUCCESS_URI, null);
} else if (auth != null) {
followNormalProcedure(request, response, auth, context);
}
}