SecurityFilterChain主要架构图
SecurityFilterChain
是 Spring Security 中的一个重要概念,它实际上代表了一个或多个安全过滤器链(Filter Chain),用于处理 HTTP 请求的安全性。
在 Spring Security 中,每个 SecurityFilterChain
对象都定义了一组安全过滤器(Security Filters),这些过滤器按顺序对请求进行处理
AbstractAuthenticationProcessingFilter
可以看到AbstractAuthenticationProcessingFilter是SecurityFilterChain当中的一个过滤器,官方的定义是AbstractAuthenticationProcessingFilter - 一个用于 认证的基本 Filter。这也让我们很好地了解了认证的高层流程以及各部分是如何协作的
可以看出这个过滤器是进行认证的,里面包含了各种过滤的方法
Authentication接口
Authentication 接口在 Spring Security中主要有两个作用。
对 AuthenticationManager 的一个输入,用于提供用户为验证而提供的凭证。当在这种情况下使用时,
isAuthenticated()
返回false
将Authentication对象传递给AuthenticationManager
代表当前 认证的用户。你可以从 SecurityContext 中获得当前的 Authentication(认证成功)
principal
: 识别用户。当用用户名/密码进行认证时,这通常是UserDetails
的一个实例。credentials
: 通常是一个密码。在许多情况下,这在用户被认证后被清除,以确保它不会被泄露。authorities
:GrantedAuthority
实例是用户被授予的高级权限。两个例子是角色(role)和作用域(scope)
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
GrantedAuthority
GrantedAuthority 实例是用户被授予的高级权限。两个例子是角色(role)和作用域(scope)。
你可以从Authentication.getAuthorities()法中获得 GrantedAuthority 实例。这个方法提供了一个 GrantedAuthority 对象的集合。毫不奇怪,GrantedAuthority 是授予委托人的一种权限。这种 授权通常是 “roles”,例如 ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。这些角色后来被配置为Web 授权、方法授权和域对象授权。 Spring Security 的其他部分会解释这些授权并期望它们存在。当使用基于用户名/密码的 认证时, GrantedAuthority 实例通常由 UserDetailsService 加载。
AuthenticationManager接口
前面提到的Authentication对象作为AuthenticationManager的输入,这个类的作用是对输入的Authentication对象进行认证;
官方解释:AuthenticationManager 是定义 Spring Security 的 Filter 如何执行 认证 的API。返回的 认证是由调用 AuthenticationManager 的控制器(即 Spring Security的 Filter 实例)在 SecurityContextHolder 上设置的。如果你不与 Spring Security 的 Filter 实例集成,你可以直接设置 SecurityContextHolder,不需要使用 AuthenticationManager。
AuthenticationManager 的实现可以是任何东西,(可以定义自己的AuthenticationManager,只需实现它,并注入到spring容器,然后可在SecurityContextHolder 上设置),例如下面
ProviderManager
ProviderManager是最常用的AuthenticationManager的实现(就像我我们定义自己的AuthenticationManager,如上图)。ProviderManager 委托给一个 ListAuthenticationProvider实例(这里的意思该类里面会有一个AuthenticationProvider的集合),查看源码如图
每个 AuthenticationProvider 都有机会表明 认证应该是成功的、失败的,或者表明它不能做出决定并允许下游的 AuthenticationProvider 来决定。如果配置的 AuthenticationProvider 实例中没有一个能进行 认证,那么认证就会以 ProviderNotFoundException 而失败,这是一个特殊的 AuthenticationException,表明 ProviderManager 没有被配置为支持被传入它的 Authentication 类型(这里的意思大概是,会遍历List<AuthenticationProvider> providers,依次认证,如果当前不能作出决定进行认证,则交由给下一个AuthenticationProvider认证,如果没有一个能进行认证,则会 ProviderNotFoundException 而失败)
在实践中,每个 AuthenticationProvider 都知道如何执行特定类型的 认证。例如,一个 AuthenticationProvider 可能能够验证一个用户名/密码,而另一个可能能够验证一个 SAML 断言。这让每个 AuthenticationProvider 在支持多种类型的 认证的同时,可以做一种非常具体的认证类型,并且只暴露一个 AuthenticationManager Bean。
ProviderManager 还允许配置一个可选的父级 AuthenticationManager,在没有 AuthenticationProvider 可以执行 认证的情况下,可以参考它。父级可以是任何类型的 AuthenticationManager,但它通常是 ProviderManager 的一个实例
事实上,多个 ProviderManager 实例可能共享同一个父级 AuthenticationManager。这在有多个 SecurityFilterChain 实例的场景中有些常见,这些实例有一些共同的 认证(共享的父 AuthenticationManager),但也有不同的认证机制(不同的 ProviderManager 实例)。
默认情况下,ProviderManager 会尝试从 Authentication 对象中清除任何敏感的凭证信息,该对象由成功的 认证请求返回。这可以防止密码等信息在 HttpSession 中保留超过必要的时间。
当你使用用户对象的缓存时,这可能会导致问题,例如,在一个无状态的应用程序中提高性能。如果 Authentication 包含对缓存中的一个对象的引用(比如 UserDetails 实例),而这个对象的凭证已经被删除,那么就不可能再针对缓存的值进行 认证。如果你使用一个缓存,你需要考虑到这一点。一个明显的解决方案是,首先在缓存实现中或在创建返回的 Authentication 对象的 AuthenticationProvider 中制作一个对象的副本。另外,你可以禁用 ProviderManager 上的 eraseCredentialsAfterAuthentication 属性。
在 ProviderManager 类中,最后一个变量 eraseCredentialsAfterAuthentication 是用来指定在认证成功后是否擦除 Authentication 对象中的凭据信息的标志位。
具体来说,当 eraseCredentialsAfterAuthentication 设置为 true 时,在成功进行身份验证后,Authentication 对象中的凭据信息(通常是密码)将被擦除或清除。这样做的目的是为了增加安全性,避免在系统的其他部分中意外泄露或暴露凭据信息。
在 Spring Security 中,默认情况下,ProviderManager 的 eraseCredentialsAfterAuthentication 默认为 true。这意味着一旦用户成功通过身份验证,其 Authentication 对象中的密码等敏感信息会被清除。这种做法可以防止在认证成功后密码继续存在于内存中,从而降低了安全风险
AuthenticationProvider
你可以在 ProviderManager 中注入多个 AuthenticationProvider 实例。每个 AuthenticationProvider 都执行一种特定类型的 认证。例如, DaoAuthenticationProvider 支持基于用户名/密码的 认证,而 JwtAuthenticationProvider 支持认证JWT令牌
用 AuthenticationEntryPoint 请求凭证
AuthenticationEntryPoint 用于发送一个要求客户端提供凭证的HTTP响应。
有时,客户端会主动包含凭证(如用户名和密码)来请求资源。在这些情况下, Spring Security 不需要提供要求客户端提供凭证的HTTP响应,因为这些凭证已经被包括在内。
在其他情况下,客户端向他们未被 授权访问的资源发出未经 认证的请求。在这种情况下, AuthenticationEntryPoint 的实现被用来请求客户端的凭证。 AuthenticationEntryPoint 的实现可能会执行 重定向到一个登录页面,用 WWW-Authenticate 头来响应,或采取其他行动
解读:这段话的意思是关于 AuthenticationEntryPoint 在 Spring Security 中的作用和使用场景的解释。让我们逐句解释:
1. AuthenticationEntryPoint 是 Spring Security 中的一个接口,用于处理未经身份验证的请求。当客户端请求一个需要认证的资源时,但未提供认证凭据(如用户名和密码)时,`AuthenticationEntryPoint` 负责向客户端发送一个响应,要求客户端提供凭证,通常是通过 HTTP 401 状态码。
2. 有时,客户端会主动包含凭证(如用户名和密码)来请求资源。在这些情况下, Spring Security 不需要提供要求客户端提供凭证的HTTP响应,因为这些凭证已经被包括在内。
当客户端在请求中已经包含了有效的凭证(如基本认证中的用户名和密码),Spring Security 就无需再发送要求提供凭证的 HTTP 响应。这种情况下,Spring Security 可以直接使用客户端提供的凭证进行认证,而不需要触发 AuthenticationEntryPoint。
3. 在其他情况下,客户端向他们未被授权访问的资源发出未经认证的请求。在这种情况下, AuthenticationEntryPoint 的实现被用来请求客户端的凭证。
如果客户端请求的资源需要认证,但客户端未提供有效的凭证或者提供的凭证不足以授权访问该资源,Spring Security 就会使用配置的 AuthenticationEntryPoint 来处理这种情况。这时,`AuthenticationEntryPoint` 可能会执行一些操作,如重定向到登录页面、发送 WWW-Authenticate 头部以触发浏览器弹出认证对话框,或者返回其他自定义的响应来要求客户端提供正确的凭证
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter 被用作验证用户凭证的基础 Filter。在 认证凭证之前, Spring Security 通常通过使用AuthenticationEntryPoint 来请求凭证。
接下来,AbstractAuthenticationProcessingFilter 可以对提交给它的任何 认证请求进行认证。
当用户提交他们的凭证时,AbstractAuthenticationProcessingFilter 会从 HttpServletRequest 中创建一个要认证的Authentication。创建的认证的类型取决于 AbstractAuthenticationProcessingFilter 的子类。例如,UsernamePasswordAuthenticationFilter从 HttpServletRequest 中提交的 username 和 password 创建一个 UsernamePasswordAuthenticationToken。
接下来,Authentication 被传入 AuthenticationManager,以进行 认证。
如果 认证失败,则为 Failure。
SecurityContextHolder 被清空。
RememberMeServices.loginFail 被调用。如果没有配置记住我(remember me),这就是一个无用功。请参阅 rememberme 包。
AuthenticationFailureHandler 被调用。参见 AuthenticationFailureHandler 接口。
如果认证成功,则为 Success。
SessionAuthenticationStrategy 被通知有新的登录。参见 SessionAuthenticationStrategy 接口。
Authentication 是在 SecurityContextHolder 上设置的。后来,如果你需要保存 SecurityContext 以便在未来的请求中自动设置,必须显式调用 SecurityContextRepository#saveContext。参见 SecurityContextHolderFilter 类。
RememberMeServices.loginSuccess 被调用。如果没有配置 remember me,这就是一个无用功。
ApplicationEventPublisher 发布一个 InteractiveAuthenticationSuccessEvent 事件。
AuthenticationSuccessHandler 被调用。