• Spring Security认证架构介绍


    在之前的Spring Security:总体架构中,我们讲到Spring Security整个架构是通过Bean容器和Servlet容器对过滤器的支持来实现的。我们将从过滤器出发介绍Spring Security的Servlet类型的认证架构。

    1.AbstractAuthenticationProcessingFilter

    AbstractAuthenticationProcessingFilter就是一个SecurityFilterChain中的过滤器,被用作验证用户凭证的基础 Filter。

    示意图:

    (当用户发送请求时,AbstractAuthenticationProcessingFilter会尝试提取Authentication对象;如果用户未经身份验证或提供的凭证无效,AbstractAuthenticationProcessingFilter 使用 AuthenticationEntryPoint 来生成适当的响应或重定向用户到登录页面。)

    1. 生成Authentication等待校验:当用户提交他们的凭证时,AbstractAuthenticationProcessingFilter 会从 HttpServletRequest 中创建一个要认证的Authentication(Authentication的第一种用法:封装待校验的用户信息,等待认证)。创建的Authentication认证的类型取决于 AbstractAuthenticationProcessingFilter 的子类。
      1. 例如,UsernamePasswordAuthenticationFilter从 HttpServletRequest 中提交的 username 和 password 创建一个 UsernamePasswordAuthenticationToken,传递给ProviderManager。
    2. 校验Authentication:接下来,Authentication 被传入 AuthenticationManager,以进行认证。
      1. 例如,UsernamePasswordAuthenticationToken给了ProviderManager,ProviderManager选择了能处理这类AuthenticationToken的AuthenticationProvider进行校验,如DaoAuthenticationProvider,DaoAuthenticationProvider调用服务层方法获得具体的用户信息,然后进行校验,最后生成一个新的UsernamePasswordAuthenticationToken;如果认证失败,也可直接抛出异常throw new BadCredentialsException("Bad credentials");
    3. 认证结果处理:
      1. 认证失败:
        1. 清空SecurityContextHolder。(SecurityContextHolder可理解为是用于储存认证完成后的Authentication的环境)
        2. 如果配置了记住我(remember me),RememberMeServices.loginFail 方法会被调用。
        3. 调用 AuthenticationFailureHandler 来处理认证失败的情况,例如,生成适当的错误响应或执行自定义操作。
      2. 认证成功:
        1. SessionAuthenticationStrategy 被通知有新的登录,这是用于管理会话的策略。
        2. 验证成功后,Authentication 对象被保存在 SecurityContextHolder 中,以便后续请求可以访问已经认证的用户信息。
        3. 如果配置了记住我(remember me),RememberMeServices.loginSuccess 方法会被调用。
        4. 一个 InteractiveAuthenticationSuccessEvent 事件被发布,可以用于处理认证成功的事件。
        5. 最后,AuthenticationSuccessHandler 被调用,用于处理认证成功后的操作,例如,重定向用户或生成自定义响应。

    接下来介绍过滤器中用到的各种组件。

    2.SecurityContextHolder与SecurityContext

    SecurityContextHolder用于存储全局的用户认证。

    一个 SecurityContext 包含一个 Authentication 对象,如果 SecurityContext 包含一个 Authentication,该 SecurityContext 就被用作当前认证的用户。

    直接在SecurityContextHolder设置用户,来表明用户已被认证:

    1. SecurityContext context = SecurityContextHolder.createEmptyContext(); //创建一个空的 SecurityContext
    2. Authentication authentication = new TestingAuthenticationToken("username", "password", "ROLE_USER"); //创建一个新的 Authentication 对象
    3. context.setAuthentication(authentication);
    4. SecurityContextHolder.setContext(context); //在 SecurityContextHolder 上设置 SecurityContext。

    提取当前用户信息:

    1. SecurityContext context = SecurityContextHolder.getContext();
    2. Authentication authentication = context.getAuthentication();
    3. String username = authentication.getName();
    4. Object principal = authentication.getPrincipal();
    5. Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

    SecurityContextHolder使用每个线程的ThreadLocal来存储对应线程的SecurityContext。这意味着 SecurityContext 对同一线程中的方法总是可用的,即使 SecurityContext 没有被明确地作为参数传递给这些方法。

    (注意:有些应用程序可能不希望一个线程对应一个安全上下文和一个用户,可以在启动时用策略模式配置 SecurityContextHolder——设置一个系统属性或调用SecurityContextHolder 的一个静态方法,以指定存储上下文的方式,如SecurityContextHolder.MODE_GLOBAL 策略将安全上下文信息将存储在一个静态的全局变量中,使其在整个应用程序中都全局可见。查看有哪些策略可以看SecurityContextHolder 的 JavaDoc。)

    3.Authentication

    Authentication有两个作用:

    1. 存储待校验的用户凭证(isAuthenticated() 返回 false),保存了用户刚提交的用来验证的信息,会由AuthenticationManager判定是否通过验证。
    2. 存储已校验的用户凭证,代表当前用户,可从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 来决定。

    我们可以自定义一个AuthenticationManager,来决定AuthenticationProvider的顺序、匹配方式等,如:(当然,都是以新版推荐的重写FilterChain的方式进行的)

    1. @EnableWebSecurity
    2. @Configuration
    3. public class MySecurityConfig {
    4. @Bean
    5. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    6. http.authenticationManager(new AuthenticationManager() {
    7. @Override
    8. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    9. MyUserNamePasswordAuthenticationProvider provider1 = new MyUserNamePasswordAuthenticationProvider();
    10. if(authentication instanceof UsernamePasswordAuthenticationToken){
    11. return provider1.authenticate(authentication);
    12. }
    13. throw new BadCredentialsException("so?");
    14. }
    15. });
    16. http.authorizeRequests(authorizeRequests ->
    17. authorizeRequests
    18. .anyRequest().authenticated()
    19. )
    20. .formLogin(); // 使用表单登录
    21. return http.build();
    22. }
    23. }

    也可以直接自定义AuthenticationProvider,让默认实现的ProviderManager进行管理:

    1. @EnableWebSecurity
    2. @Configuration
    3. public class MySecurityConfig {
    4. @Bean
    5. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    6. http.authenticationProvider(new MyUserNamePasswordAuthenticationProvider());
    7. http.authorizeRequests(authorizeRequests ->
    8. authorizeRequests
    9. .anyRequest().authenticated()
    10. )
    11. .formLogin(); // 使用表单登录
    12. return http.build();
    13. }
    14. }

  • 相关阅读:
    机器学习实践八:基于线性回归预测波士顿房价
    【题目2】 大衍数列,斐波拉契数列等,用VBA 和python解决
    快手怎么涨粉最快?10个实用方法让你迅速积累粉丝
    【分布式应用】消息队列之卡夫卡 + EFLFK集群部署
    Spring Web MVC入门
    基于Java的智能仓库(进销存)管理系统设计与实现(源码+lw+部署文档+讲解等)
    VLSI 半定制设计方法 与 全定制设计方法【VLSI】
    Android系统10 RK3399 init进程启动(二十七) SeAndroid编译规则目录
    【muduo源码剖析】Thread/ThreadPool源码解析
    微信小程序购物/超市/餐饮/酒店商城开发搭建过程和需求
  • 原文地址:https://blog.csdn.net/lifetime_gear/article/details/133959618