Spring Security 认证流程闭环与调用链路详解
书 接 上 回、我 们 分 析 下 AuthController.@PostMapping("/login") 接 口 作 为 切 入 点 进 行 深 入 浅 出 剖 析 ~ ~
下 面 我 们 以 小 明 的 authenticateUser 方 法 为 入 口 , 详 细 拆 解 认 证 执 行 的 ** 完 整 流 程 闭 环 ** , 并 深 入 分 析 Authentication authentication = authenticationManager.authenticate(...) 的 ** 调 用 链 路 ** , 同 时 说 明 ** 自 定 义 类 ** 在 其 中 的 作 用 。 代 码 块 中 汉 字 正 常 书 写 , 代 码 块 外 汉 字 间 保 持 2 个 空 格 。
一 、 完 整 认 证 流 程 闭 环
认 证 流 程 从 用 户 提 交 登 录 请 求 开 始 , 到 返 回 JWT 响 应 结 束 , 形 成 如 下 闭 环 :
流 程 闭 环 详 细 步 骤 ( 结 合 代 码 )
1. 接 收 登 录 请 求 ( 自 定 义 DTO 入 口 )
用 户 通 过 前 端 提 交 用 户 名 和 密 码 , 请 求 体 被 @RequestBody LoginRequest loginRequest 接 收 。
** 自 定 义 类 LoginRequest 作 用 ** : 封 装 登 录 请 求 参 数 ( 用 户 名 、 密 码 ) , 代 码 如 下 :
public class LoginRequest {
private String username; // 用 户 名
private String password; // 密 码
// Getters and Setters
}
2. 创 建 认 证 请 求 对 象 ( Spring Security 自 带 类 )
通 过 new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()) 创 建 认 证 请 求 对 象 。
** Spring Security 自 带 类 UsernamePasswordAuthenticationToken 作 用 ** :
- 来 源 :
org.springframework.security.authentication.UsernamePasswordAuthenticationToken - 作 用 : 封 装 用 户 名 密 码 认 证 请 求 , 初 始 状 态 下
authorities( 权 限 ) 为 空 , 相 当 于 “ 未 盖 章 的 通 行 证 ” 。
3. 调 用 AuthenticationManager.authenticate() ( 核 心 调 用 链 路 , 下 文 详 解 )
将 上 述 UsernamePasswordAuthenticationToken 传 入 authenticationManager.authenticate() , 触 发 认 证 流 程 。
4. 认 证 成 功 后 设 置 安 全 上 下 文
认 证 成 功 返 回 填 充 完 权 限 的 Authentication 对 象 , 通 过 SecurityContextHolder.getContext().setAuthentication(authentication) 绑 定 到 当 前 线 程 。
5. 生 成 JWT 令 牌 ( 自 定 义 JwtService )
调 用 jwtService.generateToken(authentication) 生 成 JWT , 用 于 后 续 无 状 态 认 证 。
** 自 定 义 类 JwtService 作 用 ** : 封 装 JWT 生 成 / 验 证 逻 辑 , 核 心 方 法 generateToken(Authentication authentication) 从 authentication 中 提 取 用 户 名 、 权 限 等 信 息 , 按 规 则 生 成 签 名 令 牌 。
6. 封 装 响 应 数 据 ( 自 定 义 DTO )
从 authentication.getPrincipal() 获 取 UserDetailsImpl ( 自 定 义 用 户 详 情 类 ) , 提 取 用 户 ID 、 用 户 名 、 邮 箱 、 角 色 等 信 息 , 封 装 为 JwtResponse 返 回 。
** 自 定 义 类 作 用 ** :
UserDetailsImpl: 实 现 Spring Security 的UserDetails接 口 , 封 装 用 户 基 础 信 息 ( ID 、 用 户 名 、 邮 箱 ) 和 权 限 ( 角 色 / 权 限 字 符 串 ) , 代 码 示 例 :
public class UserDetailsImpl implements UserDetails {
private Long id;
private String username;
private String email;
private String password;
private Collection<? extends GrantedAuthority> authorities;
// 实现UserDetails接口方法(getAuthorities()等)
// Getters and Setters
}
JwtResponse: 封 装 JWT 响 应 数 据 ( token 、 用 户 ID 、 用 户 名 、 邮 箱 、 角 色 列 表 ) , 代 码 示 例 :
public class JwtResponse {
private String token;
private Long id;
private String username;
private String email;
private List<String> roles;
// Constructor, Getters and Setters
}
7. 返 回 响 应 与 异 常 处 理
认 证 成 功 返 回 ResponseEntity.ok(response) ; 失 败 ( 如 密 码 错 误 ) 捕 获 BadCredentialsException , 返 回 401 错 误 。
二 、 AuthenticationManager.authenticate(...) 调 用 链 路 详 解
这 段 代 码 是 认 证 流 程 的 ** 核 心 枢 纽 ** , 其 内 部 调 用 链 路 如 下 :
调 用 链 路 示 意 图
authenticationManager.authenticate(token)
↓
AuthenticationManager ( 接 口 , 默 认 实 现 ProviderManager )
↓ 遍 历 注 册 的 AuthenticationProvider , 找 到 支 持 UsernamePasswordAuthenticationToken 的 Provider
DaoAuthenticationProvider ( Spring Security 自 带 , 处 理 用 户 名 密 码 认 证 )
↓ 调 用 UserDetailsService.loadUserByUsername(username) ( 自 定 义 实 现 )
UserServiceImpl ( 自 定 义 , 实 现 UserDetailsService )
↓ 从 数 据 库 查 询 用 户 信 息 , 封 装 为 UserDetailsImpl 返 回
DaoAuthenticationProvider
↓ 用 PasswordEncoder.matches(rawPassword, encodedPassword) 验 证 密 码
PasswordEncoder ( 自 定 义 BCryptPasswordEncoder )
↓ 验 证 通 过 , 构 建 已 认 证 Authentication 对 象
new UsernamePasswordAuthenticationToken(principal, credentials, authorities)
↓ 返 回 给 authenticationManager.authenticate()
Authentication ( 认 证 成 果 )
链 路 关 键 环 节 注 释
1. AuthenticationManager : 认 证 总 调 度 员
- ** 来 源 ** : Spring Security 核 心 接 口
org.springframework.security.authentication.AuthenticationManager, 默 认 实 现 为ProviderManager。 - ** 作 用 ** : 接 收
Authentication认 证 请 求 , 协 调 多 个AuthenticationProvider( 认 证 提 供 者 ) 完 成 认 证 。 - ** 入 参 ** : 未 认 证 的
UsernamePasswordAuthenticationToken( 含 用 户 名 、 明 文 密 码 ) 。 - ** 出 参 ** : 认 证 成 功 后 的
Authentication对 象 ( 含 用 户 详 情 、 权 限 集 合 ) 。
2. DaoAuthenticationProvider : 认 证 执 行 者 ( Spring Security 自 带 )
- ** 来 源 ** :
org.springframework.security.authentication.dao.DaoAuthenticationProvider。 - ** 作 用 ** : 处 理 用 户 名 密 码 认 证 的 具 体 逻 辑 , 依 赖
UserDetailsService和PasswordEncoder。 - ** 核 心 步 骤 ** :
a. 调 用UserDetailsService.loadUserByUsername(username)查 询 用 户 ;
b. 用PasswordEncoder比 对 明 文 密 码 与 数 据 库 中 的 加 密 密 码 ;
c. 验 证 通 过 后 , 构 建 已 认 证 的UsernamePasswordAuthenticationToken( 填 充authorities权 限 ) 。
3. UserDetailsService : 用 户 信 息 查 询 器 ( 自 定 义 实 现 )
- ** 来 源 ** : Spring Security 核 心 接 口
org.springframework.security.core.userdetails.UserDetailsService。 - ** 自 定 义 实 现 类
UserServiceImpl作 用 ** : 从 数 据 库 查 询 用 户 信 息 , 封 装 为UserDetails对 象 ( 此 处 为UserDetailsImpl) 返 回 。 - ** 核 心 方 法 ** :
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 从数据库查询用户(自定义User实体类)
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
// 2. 封装为UserDetailsImpl(含权限)
return UserDetailsImpl.build(user);
}
4. PasswordEncoder : 密 码 验 证 器 ( 自 定 义 配 置 )
- ** 来 源 ** : Spring Security 核 心 接 口
org.springframework.security.crypto.password.PasswordEncoder, 此 处 用 自 定 义BCryptPasswordEncoder。 - ** 作 用 ** : 验 证 明 文 密 码 与 数 据 库 中 BCrypt 加 密 密 码 是 否 匹 配 。
- ** 调 用 代 码 ** :
if (!passwordEncoder.matches(loginRequest.getPassword(), userDetails.getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
5. 构 建 认 证 后 Authentication 对 象
认 证 通 过 后 , DaoAuthenticationProvider 创 建 ** 已 认 证 ** 的 UsernamePasswordAuthenticationToken :
// 参数:principal=用户详情(UserDetailsImpl),credentials=null(密码已清空),authorities=用户权限
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
三 、 自 定 义 类 在 流 程 中 的 核 心 作 用 总 结
| ** 自 定 义 类 ** | ** 作 用 描 述 ** | ** 关 键 方 法 / 属 性 ** |
|---|---|---|
LoginRequest |
封 装 登 录 请 求 参 数 ( 用 户 名 、 密 码 ) | getUsername() 、 getPassword() |
UserDetailsImpl |
实 现 UserDetails 接 口 , 封 装 用 户 详 情 ( ID 、 用 户 名 、 邮 箱 、 权 限 ) |
getAuthorities() ( 返 回 权 限 集 合 ) 、 getUsername() |
UserServiceImpl |
实 现 UserDetailsService , 从 数 据 库 查 询 用 户 并 封 装 为 UserDetailsImpl |
loadUserByUsername(String username) |
JwtService |
生 成 / 验 证 JWT 令 牌 , 封 装 JWT 算 法 逻 辑 | generateToken(Authentication authentication) |
JwtResponse |
封 装 JWT 响 应 数 据 ( token 、 用 户 信 息 、 角 色 ) | token 、 id 、 roles |
💡 小 明 的 流 程 体 会
认 证 流 程 闭 环 像 一 场 “ 身 份 验 证 演 习 ” :
- 用 户 递 交 “ 身 份 申 请 ” (
LoginRequest) ; - 框 架 发 放 “ 临 时 通 行 证 ” (
UsernamePasswordAuthenticationToken) ; - “ 认 证 官 ” (
AuthenticationManager) 调 度 “ 审 查 员 ” (DaoAuthenticationProvider) ; - “ 审 查 员 ” 查 阅 “ 档 案 库 ” (
UserServiceImpl查 数 据 库 ) , 用 “ 密 码 对 比 仪 ” (PasswordEncoder) 验 证 ; - 通 过 后 发 放 “ 正 式 身 份 证 ” ( 认 证 后
Authentication) , 存 入 “ 工 作 台 ” (SecurityContextHolder) ; - 最 后 制 作 “ 长 期 通 行 证 ” (
JWT) 返 还 用 户 。
整 个 过 程 中 , 自 定 义 类 像 “ 业 务 胶 水 ” , 将 Spring Security 的 通 用 认 证 能 力 与 项 目 的 数 据 库 、 业 务 逻 辑 绑 定 , 既 复 用 框 架 核 心 能 力 , 又 满 足 个 性 化 需 求 。
❤️ 如果你喜欢这篇文章,请点赞支持! 👍 同时欢迎关注我的博客,获取更多精彩内容!
本文来自博客园,作者:佛祖让我来巡山,转载请注明原文链接:https://www.cnblogs.com/sun-10387834/p/19256895

浙公网安备 33010602011771号