后端项目文件详解
这是一个基于 Spring Boot 3.2.0 的博客后端项目,使用 MySQL 数据库,实现了管理员登录认证功能。
📁 项目结构
backend/
├── pom.xml # Maven 项目配置
├── src/main/java/com/blog/
│ ├── BlogApplication.java # 启动类
│ ├── config/
│ │ ├── JwtAuthenticationFilter.java # JWT 认证过滤器
│ │ └── SecurityConfig.java # Spring Security 配置
│ ├── controller/
│ │ └── AuthController.java # 认证控制器
│ ├── entity/
│ │ └── AdminUser.java # 管理员用户实体
│ ├── exception/
│ │ └── GlobalExceptionHandler.java # 全局异常处理
│ ├── mapper/
│ │ └── AdminUserMapper.java # 用户 Mapper
│ ├── service/
│ │ └── AuthService.java # 认证服务
│ └── util/
│ └── JwtUtil.java # JWT 工具类
└── src/main/resources/
├── application.yml # 应用配置
└── schema.sql # 数据库脚本
📄 详细文件说明
1. [pom.xml](file:///d:/0Document/project/Blog/backend/pom.xml) - Maven 项目配置
这是项目的依赖管理文件,定义了项目的基本信息和所有依赖。
主要配置:
| 配置项 | 值 |
|---|---|
| Spring Boot 版本 | 3.2.0 |
| Java 版本 | 17 |
| 项目版本 | 1.0.0 |
核心依赖:
| 依赖 | 用途 |
|---|---|
spring-boot-starter-web |
Web 开发(REST API) |
spring-boot-starter-security |
安全认证 |
mybatis-plus-spring-boot3-starter |
MyBatis Plus ORM 框架 |
mysql-connector-j |
MySQL 数据库驱动 |
jjwt (0.12.3) |
JWT Token 生成与解析 |
lombok |
简化代码(自动生成 getter/setter) |
2. [BlogApplication.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/BlogApplication.java) - 启动类
项目的入口类,使用 @SpringBootApplication 注解标注。
关键点:
@MapperScan("com.blog.mapper")- 自动扫描 Mapper 接口,简化数据库操作
java
@SpringBootApplication
@MapperScan("com.blog.mapper")
public class BlogApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApplication.class, args);
}
}
3. [JwtAuthenticationFilter.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/config/JwtAuthenticationFilter.java) - JWT 认证过滤器
这是一个 Spring Filter,每次请求都会经过此过滤器,用于验证 JWT Token。
工作流程:
请求 → 提取 Header 中的 Bearer Token → 验证 Token → 解析用户信息 → 存入 request 属性 → 放行
核心逻辑:
- 从请求头
Authorization中提取Bearer {token} - 调用
jwtUtil.validateToken(token)验证 Token 有效性 - 解析出
userId和username,存入request.setAttribute() - 放行请求到后续处理器
4. [SecurityConfig.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/config/SecurityConfig.java) - Spring Security 配置
配置 Spring Security 的行为,包括认证策略和过滤器链。
核心配置:
| 配置项 | 值 | 说明 |
|---|---|---|
| CSRF | 禁用 | 因为是前后端分离 API 项目 |
| Session | STATELESS | 无状态,不创建 Session |
| 认证端点 | /api/v1/auth/** |
公开,无需认证 |
| 其他端点 | authenticated() |
需要登录认证 |
| 密码加密 | BCrypt | 单向哈希加密 |
java
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll() // 登录接口公开
.anyRequest().authenticated() // 其他接口需要认证
)
5. [AuthController.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/controller/AuthController.java) - 认证控制器
处理认证相关的 HTTP 请求。
接口列表:
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/auth/login |
用户登录 |
| GET | /api/v1/auth/verify |
验证 Token |
登录请求格式:
json
{
"username": "admin",
"password": "admin123"
}
登录响应格式:
json
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"expiresIn": 86400
},
"message": "登录成功"
}
6. [AdminUser.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/entity/AdminUser.java) - 管理员用户实体
对应数据库 admin_user 表的实体类,使用 MyBatis-Plus 注解。
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 主键,自增 |
| username | String | 用户名,唯一 |
| password | String | BCrypt 加密后的密码 |
| nickname | String | 昵称 |
| avatar | String | 头像 URL |
| status | Integer | 状态:0-禁用,1-正常 |
| createTime | LocalDateTime | 创建时间 |
| updateTime | LocalDateTime | 更新时间 |
注解说明:
@TableName("admin_user")- 映射表名@TableId(type = IdType.AUTO)- 主键自增@TableField(fill = ...)- 自动填充时间字段
7. [AdminUserMapper.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/mapper/AdminUserMapper.java) - 用户 Mapper
MyBatis-Plus 的 Mapper 接口,继承 BaseMapper<AdminUser>。
无需编写 SQL,MyBatis-Plus 自动提供:
selectById(id)- 根据 ID 查询selectOne(queryWrapper)- 条件查询insert(entity)- 插入updateById(entity)- 更新deleteById(id)- 删除
8. [AuthService.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/service/AuthService.java) - 认证服务
处理登录和 Token 验证的业务逻辑。
核心方法:
| 方法 | 职责 |
|---|---|
login(username, password) |
验证用户名密码,生成 Token |
verify(token) |
验证 Token,返回用户信息 |
登录流程:
1. 根据用户名查询用户
2. 检查用户是否存在
3. 检查账号是否被禁用 (status == 0)
4. 验证密码 (BCrypt 匹配)
5. 生成 JWT Token
6. 返回 token 和过期时间
9. [JwtUtil.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/util/JwtUtil.java) - JWT 工具类
封装 JWT Token 的生成和解析操作。
核心方法:
| 方法 | 职责 |
|---|---|
generateToken(userId, username) |
生成 Token |
validateToken(token) |
验证 Token 有效性 |
parseToken(token) |
解析 Token 内容 |
getUserIdFromToken(token) |
获取用户 ID |
getUsernameFromToken(token) |
获取用户名 |
Token 包含信息:
subject- 用户 IDclaim.username- 用户名issuedAt- 签发时间expiration- 过期时间
10. [GlobalExceptionHandler.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/exception/GlobalExceptionHandler.java) - 全局异常处理
使用 @RestControllerAdvice 统一处理所有异常。
处理的异常:
| 异常类型 | HTTP 状态码 | 响应消息 |
|---|---|---|
| RuntimeException | 400 Bad Request | 异常消息 |
| Exception | 500 Internal Server Error | "服务器内部错误" |
响应格式:
json
{
"success": false,
"message": "错误信息"
}
11. [application.yml](file:///d:/0Document/project/Blog/backend/src/main/resources/application.yml) - 应用配置
Spring Boot 配置文件。
主要配置项:
| 配置项 | 值 | 说明 |
|---|---|---|
| server.port | 8080 | 服务端口 |
| datasource.url | jdbc:mysql://localhost:3306/blog | 数据库连接 |
| datasource.username | root | 数据库用户名 |
| datasource.password | 123456 | 数据库密码 |
| jwt.secret | blog-jwt-secret-key-... | JWT 密钥 |
| jwt.expiration | 86400000 | Token 有效期(24小时) |
MyBatis-Plus 配置:
map-underscore-to-camel-case: true- 下划线转驼峰logic-delete-field: isDeleted- 逻辑删除字段
12. [schema.sql](file:///d:/0Document/project/Blog/backend/src/main/resources/schema.sql) - 数据库脚本
初始化数据库表结构。
创建的表:
| 表名 | 说明 |
|---|---|
| admin_user | 管理员用户表 |
初始化管理员账号:
- 用户名:
admin - 密码:
admin123(BCrypt 加密)
🔐 认证流程图
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端 │ │ AuthController │ │ AuthService │
└──────┬──────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ POST /login │ │
│───────────────────►│ │
│ │ login() │
│ │─────────────────────►│
│ │ │
│ │ 返回 Token │
│◄─────────────────────│◄──────────────────────│
│ │ │
│ 后续请求 + Token │ │
│───────────────────►│ │
│ │ JwtAuthenticationFilter │
│ │◄───────────────────────│
│ │ 验证 Token │
│ │──────────────────────►│
│ │ 放行请求 │
│ │──────────────────────►│
📌 总结
这是一个典型的 Spring Boot + JWT 认证 脚手架项目,结构清晰,使用了:
- MyBatis-Plus 简化数据库操作
- Spring Security + JWT 实现无状态认证
- BCrypt 加密密码
- 全局异常处理 统一错误响应
适合作为博客系统或其他 Web 应用的后端基础。
JwtAuthenticationFilter 工作原理详解
1. 核心继承关系
java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
// ...
}
| 类/注解 | 作用 |
|---|---|
OncePerRequestFilter |
Spring 提供的抽象类,确保每个请求只执行一次过滤 |
@Component |
注册为 Spring Bean,自动被 Spring 管理 |
@RequiredArgsConstructor |
Lombok 自动注入 JwtUtil 依赖 |
2. 配置到 Security 过滤链
在 [SecurityConfig.java](file:///d:/0Document/project/Blog/backend/src/main/java/com/blog/config/SecurityConfig.java#L35) 中:
java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
这行代码的作用是:将自定义过滤器添加到 Spring Security 过滤链中
3. 过滤链顺序
请求进入顺序:
┌────────────────────────────────────────────────────────────────┐
│ 1. FilterChainProxy │
│ │ │
│ ▼ │
│ 2. JwtAuthenticationFilter ◄── 我们自定义的 Filter │
│ │ │
│ ▼ │
│ 3. UsernamePasswordAuthenticationFilter (Spring Security) │
│ │ │
│ ▼ │
│ ... 其他 Security Filters ... │
│ │ │
│ ▼ │
│ 4. DispatcherServlet (Controller) │
└────────────────────────────────────────────────────────────────┘
addFilterBefore() 的意思是:在某个过滤器之前执行
所以执行顺序是:JwtAuthenticationFilter → UsernamePasswordAuthenticationFilter
4. 核心方法解析
java
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// 步骤 1: 获取请求头
String authHeader = request.getHeader("Authorization");
// 步骤 2: 判断是否包含 Bearer Token
if (authHeader != null && authHeader.startsWith("Bearer ")) {
// 提取 Token (去掉 "Bearer " 前缀)
String token = authHeader.substring(7);
// 步骤 3: 验证 Token
if (jwtUtil.validateToken(token)) {
// 步骤 4: 解析用户信息
Long userId = jwtUtil.getUserIdFromToken(token);
String username = jwtUtil.getUsernameFromToken(token);
// 步骤 5: 存入 Request 供后续使用
request.setAttribute("userId", userId);
request.setAttribute("username", username);
}
}
// 步骤 6: 放行请求
filterChain.doFilter(request, response);
}
5. 完整工作流程图
┌─────────────────────────────────────────────────────────────────┐
│ 客户端请求 │
│ GET /api/v1/articles │
│ Header: Authorization: Bearer xxx.yyy.zzz │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ JwtAuthenticationFilter │
│ │
│ 1. 获取 Authorization Header │
│ "Bearer eyJhbGciOiJIUzI1NiJ9..." │
│ │ │
│ ▼ │
│ 2. 提取 Token │
│ "eyJhbGciOiJIUzI1NiJ9..." │
│ │ │
│ ▼ │
│ 3. jwtUtil.validateToken(token) │
│ ├── Token 格式正确? │
│ ├── Token 未过期? │
│ └── 签名验证通过? │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ │ 验证失败 │ 验证成功 │
│ ▼ ▼ │
│ 不解析用户信息 4. 解析用户信息 │
│ 直接放行 userId = 1 │
│ username = "admin" │
│ │ │
│ ▼ │
│ 5. 存入 request │
│ request.setAttribute("userId", 1); │
│ request.setAttribute("username", "admin"); │
│ │ │
│ ▼ │
│ 6. filterChain.doFilter() 放行 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 后续 Filter (如 Security Filter) │
│ 检查 request.getAttribute("userId") │
│ 判断用户是否已认证 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Controller │
│ 可以通过 request.getAttribute("userId") │
│ 获取当前登录用户 ID │
└─────────────────────────────────────────────────────────────────┘
6. Token 无效时会发生什么?
java
if (jwtUtil.validateToken(token)) {
// Token 有效:解析并存储用户信息
request.setAttribute("userId", userId);
request.setAttribute("username", username);
}
// 注意:这里没有 else!
// Token 无效时,不会抛出异常,只是**不存储用户信息**
// 请求会继续放行,到达 Controller
关键点: Token 无效时不会阻止请求,而是让请求继续到达 Controller。此时 Controller 可以选择:
- 允许匿名访问的接口 → 正常处理
- 需要登录的接口 → 被 Spring Security 拦截(因为没有认证信息)
7. 后续如何使用用户信息?
在 Controller 或 Service 中:
java
@RestController
@RequestMapping("/api/v1/articles")
public class ArticleController {
@GetMapping
public ResponseEntity<List<Article>> getArticles(HttpServletRequest request) {
// 从 request 中获取之前存入的用户信息
Long userId = (Long) request.getAttribute("userId");
String username = (String) request.getAttribute("username");
if (userId != null) {
// 用户已登录
System.out.println("当前用户: " + username);
}
// 业务逻辑...
}
}
8. 总结
| 步骤 | 代码 | 说明 |
|---|---|---|
| 1 | request.getHeader("Authorization") |
获取请求头 |
| 2 | authHeader.startsWith("Bearer ") |
判断是否为 JWT Token |
| 3 | jwtUtil.validateToken(token) |
验证 Token 签名和有效期 |
| 4 | jwtUtil.getUserIdFromToken(token) |
解析用户 ID |
| 5 | request.setAttribute() |
存入请求,供后续使用 |
| 6 | filterChain.doFilter() |
必须调用,否则请求会卡住 |
这个 Filter 的设计非常巧妙:验证与业务分离,只负责解析和验证 Token,不影响正常业务流程。
GlobalExceptionHandler 工作原理详解
1. 核心注解
java
@RestControllerAdvice
public class GlobalExceptionHandler {
这两个注解的作用:
| 注解 | 作用 |
|---|---|
@RestControllerAdvice |
= @ControllerAdvice + @ResponseBody - 拦截所有 @RestController 的异常 - 自动将返回值转为 JSON |
@ControllerAdvice |
Spring 提供的全局异常处理器 增强器 - 作用于所有 Controller |
2. 工作流程
┌─────────────────────────────────────────────────────────────────┐
│ HTTP 请求 │
└──────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Controller 处理请求 │
│ (可能抛出异常) │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────────────┴──────────────┐
│ 抛出异常 │ 正常返回
▼ ▼
┌───────────────────────────────────────────────────────────────┐
│ Spring MVC 异常处理机制 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ @RestControllerAdvice 拦截到异常 │ │
│ │ │ │
│ │ @ExceptionHandler(RuntimeException.class) │─────┼──► 返回 400
│ │ @ExceptionHandler(Exception.class) │─────┼──► 返回 500
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
└───────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 响应 JSON 给前端 │
└─────────────────────────────────────────────────────────────────┘
3. 异常匹配规则
@ExceptionHandler 的匹配是精确匹配:
java
@ExceptionHandler(RuntimeException.class) // 只处理 RuntimeException 及其子类
public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException e) {
// ...
}
@ExceptionHandler(Exception.class) // 处理所有未匹配到的异常(兜底)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
// ...
}
匹配优先级:
- 先查找精确匹配的异常类型
- 如果没有,向父类查找(如 RuntimeException 的父类是 Exception)
- 最后兜底到
Exception.class
4. 实际例子
场景:用户登录时输入错误密码
java
// AuthService.java 中
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new RuntimeException("密码错误"); // 抛出异常
}
处理流程:
1. Controller 调用 authService.login()
2. Service 抛出 RuntimeException("密码错误")
3. Spring 自动捕获异常
4. @RestControllerAdvice 匹配到 RuntimeException 处理方法
5. 返回 400 状态码 + JSON 响应
前端收到的响应:
json
HTTP/1.1 400 Bad Request
{
"success": false,
"message": "密码错误"
}
5. 源码追踪
@RestControllerAdvice 的位置
Spring MVC 处理流程:
DispatcherServlet.doDispatch()
│
▼
HandlerExceptionResolver 异常解析器链
│
├── 1. @ExceptionHandler 方法匹配
│
▼
返回 ModelAndView 或直接写入响应体
Spring MVC 内部会扫描所有带 @ExceptionHandler 的方法,当 Controller 抛出异常时,自动找到最匹配的处理方法。
6. 扩展:添加更多异常处理
java
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationException(
MethodArgumentNotValidException e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "参数校验失败");
response.put("errors", e.getBindingResult().getFieldErrors());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
// 处理 404 异常
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Map<String, Object>> handleNotFound(
NoHandlerFoundException e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "资源不存在");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Map<String, Object>> handleBusinessException(
BusinessException e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
response.put("code", e.getCode());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
SecurityConfig.java 工作原理详解
1. 核心注解
java
@Configuration // 声明这是一个配置类
@EnableWebSecurity // 启用 Spring Security
@RequiredArgsConstructor // Lombok - 自动注入依赖
public class SecurityConfig {
| 注解 | 作用 |
|---|---|
@Configuration |
Spring 配置文件,等价于 XML 配置 |
@EnableWebSecurity |
启用 Spring Security 自动配置 |
@RequiredArgsConstructor |
生成构造函数,自动注入 JwtAuthenticationFilter |
2. 核心配置详解
2.1 密码加密器
java
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- 提供 BCrypt 密码加密/验证功能
- 登录时用来比对用户输入的密码和数据库中的加密密码
2.2 安全过滤链(核心)
java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 1. 禁用 CSRF(前后端分离不需要)
.csrf(AbstractHttpConfigurer::disable)
// 2. 无状态会话(JWT 认证不需要 Session)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 3. 请求授权配置
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll() // 公开接口
.anyRequest().authenticated() // 其他需认证
)
// 4. 添加 JWT 过滤器
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
3. 配置项逐个解析
3.1 CSRF 禁用
java
.csrf(AbstractHttpConfigurer::disable)
| 项目 | 说明 |
|---|---|
| 什么是 CSRF | 跨站请求伪造攻击 |
| 为什么禁用 | 前后端分离项目使用 JWT,不需要 CSRF Token |
| 风险 | 无风险,JWT 本身已足够安全 |
3.2 会话策略
java
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
| 策略 | 说明 |
|---|---|
STATELESS |
无状态 - 不创建 Session - 不使用 Cookie - 每次请求都带 Token |
对比:
┌─────────────────────────────────────────────────────────┐
│ 有状态 (传统 Session) │
│ 登录 → 服务器创建 Session → 返回 SessionID → Cookie │
│ 后续请求 → 带 Cookie → 服务器验证 Session │
├─────────────────────────────────────────────────────────┤
│ 无状态 (JWT) │
│ 登录 → 服务器生成 Token → 返回给前端 │
│ 后续请求 → Header 带 Token → 服务器验证 Token │
│ ★ 服务器不存储任何会话信息 │
└─────────────────────────────────────────────────────────┘
3.3 请求授权
java
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll() // 公开
.anyRequest().authenticated() // 其他需登录
)
| 规则 | 路径 | 访问权限 |
|---|---|---|
permitAll() |
/api/v1/auth/** |
所有人可访问 |
authenticated() |
其他所有路径 | 需要登录认证 |
请求流程:
┌──────────────────────────────────────────────────────┐
│ │
│ 请求 /api/v1/auth/login │
│ │ │
│ ▼ │
│ .requestMatchers("/api/v1/auth/**").permitAll() │
│ │ │
│ ▼ 匹配到!直接放行 │
│ Controller 处理 │
│ │
├──────────────────────────────────────────────────────┤
│ │
│ 请求 /api/v1/articles │
│ │ │
│ ▼ │
│ .requestMatchers("/api/v1/auth/**").permitAll() ✗ 不匹配
│ │ │
│ ▼ │
│ .anyRequest().authenticated() │
│ │ │
│ ▼ 需要认证! │
│ 检查是否有有效 Token │
│ ├── 有 → 放行 │
│ └── 无 → 返回 401 │
│ │
└──────────────────────────────────────────────────────┘
3.4 添加 JWT 过滤器
java
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
作用:将自定义 JWT 过滤器插入到 Spring Security 过滤链中
过滤链顺序(从左到右执行):
┌─────────────────────────────────────────────────────────┐
│ 1. JwtAuthenticationFilter ◄── 我们自定义的 Filter │
│ (提取并验证 Token,存入用户信息) │
├─────────────────────────────────────────────────────────┤
│ 2. UsernamePasswordAuthenticationFilter │
│ (Spring Security 默认的表单登录过滤器) │
│ - 如果已认证,跳过 │
│ - 如果未认证且是登录请求,尝试认证 │
├─────────────────────────────────────────────────────────┤
│ 3. 其他 Security Filters... │
│ (授权检查、异常处理等) │
├─────────────────────────────────────────────────────────┤
│ 4. DispatcherServlet │
│ (到达 Controller) │
└─────────────────────────────────────────────────────────┘
4. 完整请求处理流程
客户端请求
│
▼
┌─────────────────────────────────────────────────────────┐
│ Spring Security 过滤链 │
│ │
│ 1. JwtAuthenticationFilter │
│ ├── 获取 Authorization Header │
│ ├── 提取并验证 JWT Token │
│ ├── 解析 userId/username │
│ └── 存入 request.setAttribute() │
│ │
│ 2. Spring Security 内置过滤器 │
│ └── 检查是否已认证 (isAuthenticated) │
│ │
│ 3. AuthorizationFilter │
│ └── 检查路径权限 │
│ │
└────────────────────────┬────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
验证通过 验证失败
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 放行请求 │ │ 返回 401 │
│ 到 Controller │ │ 未授权错误 │
└─────────────────┘ └─────────────────┘
5. 配置类比理解
可以把 SecurityConfig 理解为一份安检流程表:
安检流程:
┌─────────────────────────────────────────────┐
│ 1. 开放入口 vs 封闭入口 │
│ - 登录口 (permitAll) → 任何人可进 │
│ - 其他入口 (authenticated) → 凭证入场 │
│ │
│ 2. 凭证类型 │
│ - 传统:会员卡 (Session) │
│ - 现代:刷脸/刷身份证 (JWT Token) │
│ │
│ 3. 验证规则 │
│ - 检查证件有效性 │
│ - 检查是否有权限进入 │
│ │
│ 4. 异常处理 │
│ - 证件无效 → 驱逐出境 (401) │
│ - 证件过期 → 提醒续期 (提示登录) │
└─────────────────────────────────────────────┘
6. 总结
| 配置项 | 作用 |
|---|---|
@Configuration |
注册为配置类 |
@EnableWebSecurity |
启用安全功能 |
BCryptPasswordEncoder |
密码加密/验证 |
csrf().disable() |
禁用 CSRF(前后端分离) |
STATELESS |
无状态会话(JWT) |
permitAll() |
公开接口 |
authenticated() |
需要认证 |
addFilterBefore() |
添加 JWT 过滤器 |
这套配置是 JWT + Spring Security 的标准组合拳,简洁高效!
7. 总结
| 特性 | 说明 |
|---|---|
| 作用范围 | 所有 @RestController |
| 生效条件 | Controller 抛出异常 |
| 匹配规则 | 精确匹配 → 父类匹配 → 兜底匹配 |
| 返回值 | 自动转为 JSON(因为用了 @RestControllerAdvice) |
| 状态码 | 由 @ExceptionHandler 中指定的 ResponseEntity.status() 决定 |
这样前端只需要统一处理 success: false 的响应即可,无需在每个 Controller 中单独处理异常。