ETJava Beta | Java    注册   登录
  • 搜索:
  • 四、Spring Boot集成Spring Security之认证流程

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/sanxiaolq/p/18463644
    如有侵权 请联系我们删除  (页面底部联系我们)  

    二、概要说明

    1. 本文主要介绍登录登出业务流程,所以使用基于内存的用户名密码,暂不介绍授权相关内容,后续会详细介绍基于数据库的认证及授权
    2. 如何查看基于内存的默认用户名密码
    3. 如何配置基于内存的自定义用户名密码
    4. 本文与上文有强关联性,如果对过滤器链中登录相关的过滤器不熟悉的同学,请先查看三、Spring Boot集成Spring Security之过滤器链详解

    三、基于内存的用户名密码

    1、默认用户名密码

    1. 一、Spring Boot集成Spring Security之自动装配中第六节已介绍当用户未自定义认证接口时,生成默认认证接口inMemoryUserDetailsManager,其中用户名为user,密码为随机生成的uuid,项目启动时会打印在控制台中

    b57d15674a5e2bea040d35855b4b543

    1. 用户名密码源码

    image-20241012095157650

    2、自定义用户名密码

    1. 上一小节【用户名密码源码】中配置绑定可以配置自定义用户名、密码
    2. 通过配置文件配置用户名密码

    image-20241012095242326

    3、为方便测试添加测试接口TestController

    package com.yu.demo.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @GetMapping("/hello")
        public String hello() {
            return "success";
        }
    
    }
    
    

    image-20241012095447823

    四、登录登出重要概念介绍

    1. 安全上下文仓库(SecurityContextRepository):用于存储安全上下文,默认基于session实现(HttpSessionSecurityContextRepository)
    2. 安全上下文持有者(SecurityContextHolder):用于存储本次请求的安全上下文,默认基于ThreadLocal实现
    3. 安全上下文(SecurityContext):用于存储认证信息
    4. 认证信息(Authentication):用于存储用户及认证结果信息,主要实现类有
      • 用户名密码认证Token:UsernamePasswordAuthenticationToken
      • 匿名认证Token:AnonymousAuthenticationToken
    5. 登录页面请求:跳转到登录页面的请求
    6. 登录请求:在登录页面输入用户名密码后提交的请求
    7. 登出页面请求:跳转到登出页面的请求
    8. 登出请求:在登出页面确认登出提交的请求

    五、登录业务逻辑

    1、登录业务相关过滤器

    1. SecurityContextPersistenceFilter
    2. UsernamePasswordAuthenticationFilter
    3. DefaultLoginPageGeneratingFilter
    4. AnonymousAuthenticationFilter
    5. ExceptionTranslationFilter
    6. FilterSecurityInterceptor

    2、访问业务请求处理流程

    1)、访问业务请求地址被拦截,重定向到登录页面请求

    1. 浏览器访问业务请求地址:http://localhost:8080/test/hello

    2. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取安全上下文为空,创建没有认证信息的安全上下文(SecurityContextImpl)
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码

      image-20241012095700356

    3. AnonymousAuthenticationFilter处理请求:

      1. 获取安全上下文持有者中的安全上下文中的认证信息为空
      2. 创建匿名认证信息
      3. 创建新的没有认证信息的安全上下文
      4. 将第2步中的匿名认证信息设置到第3步中的安全上下文中
      5. 将第3步中的安全上下文中设置到安全上下文持有者中
      6. 执行后续过滤器链
      7. 源码

      image-20241012100012219

    4. FilterSecurityInterceptor处理请求:

      1. 验证安全上下文持有者中的安全上下文中的匿名认证信息通过
      2. 验证授权信息失败(业务请求地址未设置可以匿名访问时),抛出AccessDeniedException异常
      3. 源码

      image-20241012100452976

    5. ExceptionTranslationFilter处理请求:

      1. 捕获FilterSecurityInterceptor抛出的AccessDeniedException异常
      2. 判断是因为匿名访问导致的授权异常
      3. 创建新的没有认证信息的安全上下文
      4. 将第3步中的安全上下文中设置到安全上下文持有者中
      5. 重定向到登录页面:http://localhost:8080/login
      6. 源码

      image-20241012102138125

    6. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码

      image-20241012103447873

    7. 重定向登录页面请求:http://localhost:8080/login(GET)

    2)、重定向定页面请求,返回登录页面

    1. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取安全上下文为空,创建没有认证信息的安全上下文(SecurityContextImpl)
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码(同上)
    2. DefaultLoginPageGeneratingFilter处理请求:

      1. 判断是跳转到登录页面的请求
      2. 生成默认登录页面
      3. 返回并渲染生成的默认登录页面
      4. 源码

      image-20241012105043462

    3. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码(同上)

    3)、输入正确用户名密码,重定向到业务请求

    1. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取安全上下文为空,创建没有认证信息的安全上下文(SecurityContextImpl)
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码(同上)
    2. UsernamePasswordAuthenticationFilter处理请求:

      1. 判断需要认证(AbstractAuthenticationProcessingFilter.doFilter方法)
      2. 认证用户名密码成功,生成已认证的认证信息UsernamePasswordAuthenticationToken
      3. 创建新的没有认证信息的安全上下文
      4. 将第2步中的认证信息设置到第3步中的安全上下文中
      5. 将第3步中的安全上下文设置到安全上下文持有者中
      6. 将第3步中的安全上下文保存到局部变量安全上下文仓库中(空实现)
      7. 重定向到业务请求地址:http://localhost:8080/test/hello
      8. 源码

      image-20241012110540446

    3. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码(同上)

    4)、重定向到业务请求

    1. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取已认证的安全上下文
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码(同上)
    2. FilterSecurityInterceptor处理请求:

      1. 验证安全上下文持有者中的安全上下文中的认证信息通过
      2. 验证授权成功
      3. 调用接口返回数据
      4. 源码

      image-20241012110925382

    3. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码(同上)

    六、登出业务实现逻辑

    1、登出业务相关过滤器

    1. SecurityContextPersistenceFilter
    2. LogoutFilter
    3. DefaultLogoutPageGeneratingFilter

    2、访问登出页面请求处理流程

    1. 浏览器访问登出请求地址:http://localhost:8080/logout

    2. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取已认证的安全上下文
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码(同上)
    3. DefaultLogoutPageGeneratingFilter处理请求:

      1. 判断是跳转到登出页面的请求
      2. 生成默认登出页面
      3. 返回并渲染生成的默认登出页面
      4. 源码

      image-20241012111205378

    4. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码(同上)

    3、登出页面确认登出请求处理流程

    1)、确认登出,重定向到登录页面请求

    1. SecurityContextPersistenceFilter处理请求:

      1. 从安全上下文仓库中获取已认证的安全上下文
      2. 将第1步中获取的安全上下文设置到安全上下文持有者中
      3. 执行后续过滤器链
      4. 源码(同上)
    2. LogoutFilter处理请求:

      1. 判断是登出请求
      2. 获取安全上下文持有者中的安全上下文
      3. 登出处理器处理登出业务
        1. 删除安全上下文持有者中的安全上下文
        2. 创建没有认证信息的安全上下文
        3. 将第2步中的安全上下文保存到安全上下文仓库中
      4. 重定向到登录页面
      5. 源码

      image-20241012111435585

    3. SecurityContextPersistenceFilter处理请求:

      1. 执行chain.doFilter之后的代码
      2. 获取安全上下文持有者中的安全上下文
      3. 删除安全上下文持有者中的安全上下文
      4. 将第2步中获取的安全上下文保存到安全上下文仓库中
      5. 源码(同上)

    2)、登录页面请求

    第五节登录业务逻辑已介绍,不再赘述。

    七、说明

    1. Spring Boot集成Spring Security默认是非前后分离架构
    2. 本文介绍的流程是非前后分离版本的处理流程
    3. 前后分离处理流程较为简单
      1. 未认证时访问业务接口,返回未认证错误信息
      2. 调用登录接口成功后返回Token,此后请求头中携带此Token
      3. 调用登出接口成功后返回成功,后端将该Token失效
      4. 携带Token访问业务接口,后端验证Token成功后,调用业务接口并返回数据