四、Spring Boot集成Spring Security之认证流程

cnblogs 2024-10-14 11:09:00 阅读 93

二、概要说明

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

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

1、默认用户名密码

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

b57d15674a5e2bea040d35855b4b543

    <li>用户名密码源码

image-20241012095157650

2、自定义用户名密码

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

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

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

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

五、登录业务逻辑

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

    li>
  3. AnonymousAuthenticationFilter处理请求:

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

    image-20241012100012219

    li>
  4. FilterSecurityInterceptor处理请求:

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

    image-20241012100452976

    li>
  5. ExceptionTranslationFilter处理请求:

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

    image-20241012102138125

    li>
  6. SecurityContextPersistenceFilter处理请求:

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

    image-20241012103447873

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

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

  1. SecurityContextPersistenceFilter处理请求:

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

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

    image-20241012105043462

    li>
  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

    li>
  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

    li>
  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

    li>
  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

    li>
  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成功后,调用业务接口并返回数据


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。