SecurityContextHolder
SecurityContextHolder 是 spring security 认证模型的心脏。它包含一个 SecurityContext。
SecurityContextHolder 是 spring security 存储被认证过的用户的详情的地方, spring security 不关心 SecurityContextHolder 是怎样被填充的,一旦它被填充,它就被用作是当前的被认证用户。
最简单的方式填充 SecurityContextHolder 就是之间创建一个 SecurityContextHolder 对象,并对它进行填充,示例代码如下:
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
默认情况下,SecurityContextHolder 使用 ThreadLocal 来保存 SecurityContext ,这意味着只要在同一个线程里,SecurityContext 可以在任何地方被访问到,同时在请求处理完成后,由 FilterChainProxy 确保 SecurityContext 被清除掉。
SecurityContext
SecurityContext 从 SecurityContextHolder 中获取,SecurityContext 中包含一个 Authentication 对象。
Authentication
Authentication 接口有两个作用:
- 作为
AuthenticationManager的输入,提供用户认证用的凭证。 - 提供当前认证通过的用户信息,
Authentication可以从SecurityContext中获取到。
Authentication 中包含三个对象:
principal:标识用户,当使用用户名和密码认证的时候,这通常是一个UserDetails对象实例。credentials:提供密码,在多数情况下,认证通过后会被清空掉,为了防止密码泄露。authorities:GrantedAuthority是授予用户的高级权限。
GrantedAuthority
GrantedAuthority 实例是授予用户的高级权限。
我们可以通过 Authentication.getAuthorities() 方法来获取 GrantedAuthority, GrantedAuthority 是授予 principal 的。在使用用户名密码认证的用户,GrantedAuthority 通常用 UserDetailsService 来加载。
AuthenticationManager
AuthenticationManager 是定义来执行具体的认证的 API。认证完成后会返回一个 Authentication 对象,随后 AuthenticationManager 的调用方会将 Authentication 设置到 SecurityContextHolder 中。 如果没有集成 spring security filter 的实例。也可以直接设置 SecurityContextHolder。
ProviderManager
ProviderManager 是 AuthenticationManager 最常用的实现。
ProviderManager 委派给了 一系列的 AuthenticationManager 实例。 每个 AuthenticationManager 都有机会被指示去决定认证成功或者失败,或者指示下游的 AuthenticationManager 去决定认证是否成功。 如果没有 AuthenticationManager 实例,会抛出 ProviderNotFoundException 异常。这是一个提示没有支持这个类型的 Authentication 认证的 ProviderManager 被配置的特定的 AuthenticationException。
在实践中,每个AuthenticationProvider 都对应一种特定类型的认证。 比如:AuthenticationProvider 可能执行用户名密码认证。其他的可能执行其他类型的认证。
ProviderManager 也可以配置一个可选的父类 AuthenticationManager , 这个父类的 AuthenticationManager 是在没有可用的 AuthenticationProvider 类时,提供给服务。这个父类可以是任何类型的 AuthenticationManager ,但是它通常都是一个 ProviderManager 实例。
多个 ProviderManager 实例可以共享同一个 父类 AuthenticationManager。这种通常发生在存在多个 SecurityFilterChain 实例的情况下,并且他们的认证机制不同。
AuthenticationProvider
我们可以注入多个 AuthenticationProvider 实例到 ProviderManager中。每个 AuthenticationProvider 执行特定类型的认证。比如:DaoAuthenticationProvider 支持 用户名密码认证。 JwtAuthenticationProvider 支持 JWT token 认证。
AuthenticationEntryPoint
AuthenticationEntryPoint 被用来对来自客户端的需要 credentials 的请求发送一个响应。
在有些情况下,客户端的请求资源中本来就包含 credentials 信息,比如用户名和密码。此时 spring security 就不需要进行相应。
有些情况下,客户端发出一个没有经过认证的请求,这时候,AuthenticationEntryPoint 的实现被用作向客户端请求一个 credentials。AuthenticationEntryPoint 的实现可能是重定向到一个登录页,或者响应一个 WWW-Authenticate 头信息,或者其他行为。
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter 被用来认证用户的 credentials。在 credentials 被认证前,spring security 通常使用 AuthenticationEntryPoint 来请求一个 credentials。
然后,AbstractAuthenticationProcessingFilter 将会认证任何被提交的认证请求。
- 当用户提交 credentials 后,
AbstractAuthenticationProcessingFilter会从HttpServletRequest中创建一个Authentication。Authentication的类型取决于使用具体哪个类型的AbstractAuthenticationProcessingFilter。比如:UsernamePasswordAuthenticationFilter将会创建一个UsernamePasswordAuthenticationToken类型的 Authentication。 - 然后
Authentication被传递到AuthenticationManager中被认证。 - 如果认证失败:
- SecurityContextHolder 被清空。
RememberMeServices.loginFail被调用。AuthenticationFailureHandler被调用。
- 如果认证成功:
SessionAuthenticationStrategy被通知有一个新的登录。Authentication被设置进SecurityContextHolder。如果想在后续的请求中自动设置,必须明确手动调用SecurityContextRepository#saveContext来保存SecurityContext。RememberMeServices.loginSuccess被调用,如果没有配置,将不会有任何操作。ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent。AuthenticationSuccessHandler被调用。