我一直在从事 spring security 工作,一切都运行良好。但我只是去调试它是如何工作的。所有过滤器都通过 http 命名空间为 springsecurityfilterchain 配置。其中之一是用于提供身份验证的身份验证过滤器。我发现当一个新的登录请求到来时(没有以前的会话),身份验证过滤器被调用,但是当请求来自已经登录的用户时,即一个会话存在,那么身份验证过滤器不会被调用。我找不到“springsecurity”在哪里以及如何跳过已登录请求的此身份验证。请帮助我理解这一点。
谢谢
我一直在从事 spring security 工作,一切都运行良好。但我只是去调试它是如何工作的。所有过滤器都通过 http 命名空间为 springsecurityfilterchain 配置。其中之一是用于提供身份验证的身份验证过滤器。我发现当一个新的登录请求到来时(没有以前的会话),身份验证过滤器被调用,但是当请求来自已经登录的用户时,即一个会话存在,那么身份验证过滤器不会被调用。我找不到“springsecurity”在哪里以及如何跳过已登录请求的此身份验证。请帮助我理解这一点。
谢谢
它是由三个组合处理的Filters:
SecurityContextPersistenceFilter它Authentication
从会话中获取(如果存在)并将其放在本地线程中,SecurityContextHolder以便其他过滤器可以看到它。
如果没有Authentication在SecurityContextHolder中AnonymousAuthenticationFilter添加AnonymousAuthenticationToken一个SecurityContextHolder。
FilterSecurityInterceptorAuthentication如果中的SecurityContextHolder没有ROLE_USER当前请求的适当权限(例如 ),则引发异常。
httpSessionIntegrationFilter照顾这个。它负责维护来自同一用户的请求之间的会话。
我认为 SessionManagementFilter 可以解决这个问题:
if (!securityContextRepository.containsContext(request)) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
// The user has been authenticated during the current request, so call the session strategy
try {
sessionAuthenticationStrategy.onAuthentication(authentication, request, response);
} catch (SessionAuthenticationException e) {
// The session strategy can reject the authentication
logger.debug("SessionAuthenticationStrategy rejected the authentication object", e);
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, e);
return;
}
// Eagerly save the security context to make it available for any possible re-entrant
// requests which may occur before the current request completes. SEC-1396.
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
} else {
// No security context or authentication present. Check for a session timeout
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
if(logger.isDebugEnabled()) {
logger.debug("Requested session ID " + request.getRequestedSessionId() + " is invalid.");
}
if (invalidSessionStrategy != null) {
invalidSessionStrategy.onInvalidSessionDetected(request, response);
return;
}
}
}
}
当然,这里还有更多的东西。身份验证存储在 ThreadLocal 中:
在 Spring Security 中,存储请求之间的 SecurityContext 的责任落在了 SecurityContextPersistenceFilter 上,它默认将上下文存储为 HTTP 请求之间的 HttpSession 属性。它为每个请求将上下文恢复到 SecurityContextHolder,并且至关重要的是,在请求完成时清除 SecurityContextHolder。出于安全目的,您不应直接与 HttpSession 交互。这样做根本没有理由 - 始终使用 SecurityContextHolder 代替。
因此,SecurityContextPersistenceFilter 负责加载上下文数据(并稍后将其删除),过滤器链中的其他模块将根据这些数据做出决策。(例如跳过或通过 usermanager 服务进行身份验证)
我不是 SpringSecurity 开发人员,因此仅将此信息作为有根据的猜测:)
当向安全对象发出请求时,SessionManagementFilter 将查看 SecurityContextRepository。如果其内容与 SecurityContextHolder 匹配,则假定该用户之前已通过身份验证(如记住我或预身份验证)。
如果存储库包含安全上下文,则此过滤器不执行任何操作。
如果它什么都没有并且 ThreadLocal 包含一个身份验证对象,则它假定以前的过滤器已经以某种方式对该用户进行了身份验证。
好的问题可能仍然是实际的,因此将尝试回答:
根据我自己的研究,它看起来不像“跳过”。
首先 spring 生成过滤器链。如果您使用弹簧启动 - 只需启用调试并在启动日志中您可以看到整个过滤器链。
每个请求都通过这个链——不管你在“configure”方法和 HttpSecurity 对象中配置了什么。所以你的短语
我发现当一个新的登录请求到来时(没有以前的会话),身份验证过滤器被调用,但是当请求已经登录用户时,即一个会话存在,那么身份验证过滤器不会被调用
不是真的。将调用身份验证过滤器,但在 doFilter 内部存在检查请求,如果 URL 不用于登录 - 过滤器调用 doFilter 并“返回”。示例:您将 LoginPage 过滤器配置为“/login POST”。您可以使用不同的凭据将 POST 请求一一发送到 /login,您的应用程序会将会话重写给新用户。因此,如果您不想让用户这样做 - 您可以覆盖过滤器和一些逻辑来检查用户是否登录并调用 doFilter。