在之前的Spring Security:总体架构中,我们讲到Spring Security整个架构是通过Bean容器和Servlet容器对过滤器的支持来实现的。我们将从过滤器出发介绍Spring Security的Servlet类型的认证架构。
1.AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter就是一个SecurityFilterChain中的过滤器,被用作验证用户凭证的基础 Filter。
示意图:
(当用户发送请求时,AbstractAuthenticationProcessingFilter会尝试提取Authentication对象;如果用户未经身份验证或提供的凭证无效,AbstractAuthenticationProcessingFilter 使用 AuthenticationEntryPoint 来生成适当的响应或重定向用户到登录页面。)
- 生成Authentication等待校验:当用户提交他们的凭证时,AbstractAuthenticationProcessingFilter 会从 HttpServletRequest 中创建一个要认证的Authentication(Authentication的第一种用法:封装待校验的用户信息,等待认证)。创建的Authentication认证的类型取决于 AbstractAuthenticationProcessingFilter 的子类。例如,UsernamePasswordAuthenticationFilter从 HttpServletRequest 中提交的 username 和 password 创建一个 UsernamePasswordAuthenticationToken。
- 校验Authentication:接下来,Authentication 被传入 AuthenticationManager,以进行认证。
- 认证结果处理:
- 认证失败:
- 清空SecurityContextHolder。(SecurityContextHolder可理解为是用于储存认证完成后的Authentication的环境)
- 如果配置了记住我(remember me),RememberMeServices.loginFail 方法会被调用。
- 调用 AuthenticationFailureHandler 来处理认证失败的情况,例如,生成适当的错误响应或执行自定义操作。
- 认证成功:
- SessionAuthenticationStrategy 被通知有新的登录,这是用于管理会话的策略。
- 验证成功后,Authentication 对象被保存在 SecurityContextHolder 中,以便后续请求可以访问已经认证的用户信息。
- 如果配置了记住我(remember me),RememberMeServices.loginSuccess 方法会被调用。
- 一个 InteractiveAuthenticationSuccessEvent 事件被发布,可以用于处理认证成功的事件。
- 最后,AuthenticationSuccessHandler 被调用,用于处理认证成功后的操作,例如,重定向用户或生成自定义响应。
- 认证失败:
接下来介绍过滤器中用到的各种组件。
2.SecurityContextHolder与SecurityContext
SecurityContextHolder用于存储全局的用户认证。
一个 SecurityContext 包含一个 Authentication 对象,如果 SecurityContext 包含一个 Authentication,该 SecurityContext 就被用作当前认证的用户。
直接在SecurityContextHolder设置用户,来表明用户已被认证:
SecurityContext context = SecurityContextHolder.createEmptyContext(); //创建一个空的 SecurityContext
Authentication authentication = new TestingAuthenticationToken("username", "password", "ROLE_USER"); //创建一个新的 Authentication 对象
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); //在 SecurityContextHolder 上设置 SecurityContext。
提取当前用户信息:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
SecurityContextHolder使用每个线程的ThreadLocal来存储对应线程的SecurityContext。这意味着 SecurityContext 对同一线程中的方法总是可用的,即使 SecurityContext 没有被明确地作为参数传递给这些方法。
(注意:有些应用程序可能不希望一个线程对应一个安全上下文和一个用户,可以在启动时用策略模式配置 SecurityContextHolder------设置一个系统属性或调用SecurityContextHolder 的一个静态方法,以指定存储上下文的方式,如SecurityContextHolder.MODE_GLOBAL 策略将安全上下文信息将存储在一个静态的全局变量中,使其在整个应用程序中都全局可见。查看有哪些策略可以看SecurityContextHolder 的 JavaDoc。)
3.Authentication
Authentication有两个作用:
- 存储待校验的用户凭证(isAuthenticated() 返回 false),保存了用户刚提交的用来验证的信息,会由AuthenticationManager判定是否通过验证。
- 存储已校验的用户凭证,代表当前用户,可从SecurityContext中获得。
4.GrantedAuthority
应用了享元模式的权限实例类,代表了用户的角色(role)和作用域(scope)。
Authentication.getAuthorities()函数会返回GrantedAuthority 实例的集合。
5.AuthenticationManager、ProviderManager和AuthenticationProvider
AuthenticationManager 是定义 Spring Security 的 Filter 如何校验认证的接口,最常见的实现是ProviderManager。
ProviderManager会存储一个AuthenticationProvider的List,每个 AuthenticationProvider 执行特定类型的认证(如DaoAuthenticationProvider 支持基于用户名/密码的认证,而 JwtAuthenticationProvider 支持认证JWT令牌)。
每个 AuthenticationProvider 都有机会表明认证应该是成功的、失败的,或者表明它不能做出决定并允许下游的 AuthenticationProvider 来决定。