Week 1-8: Spring Boot 学习计划完整项目

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 23:45:17 +08:00
commit f95aa18724
201 changed files with 18595 additions and 0 deletions

216
week5/教案.md Normal file
View File

@@ -0,0 +1,216 @@
# Week 5Spring 全家桶核心组件 —— 安全、缓存、监控与进阶
**目标**:在 Week 4 的全栈项目基础上,引入 Spring Security + JWT 实现认证授权,集成 Redis 缓存,接入 Actuator 监控,自定义校验注解,掌握 MyBatis-Plus 逻辑删除与分页。
**前置**:已完成 Week 4 的 Spring Boot + ORM 双轨 + MVC 全栈项目。
**学习成果**:一个完整的"学生管理系统 v2",具备:
- JWT 令牌认证(登录 / 注册)
- RBAC 角色控制ADMIN 可增删改USER 只读)
- Redis 缓存加速查询
- Actuator 健康检查与监控
- @ValidScore 自定义校验注解
- MyBatis-Plus 逻辑删除
---
## Day 1Spring Security 架构 & 过滤器链
**知识点**
- Spring Security 核心概念Authentication认证vs Authorization授权
- SecurityFilterChain过滤器链的工作原理
- 关键过滤器UsernamePasswordAuthenticationFilter、SecurityContextHolder
- 无状态会话STATELESSvs 有状态会话
- CSRF 的概念及何时禁用(前后端分离 API
- BCrypt 密码编码器的不可逆特性
**动手 → 理解**
1. 阅读 `SecurityConfig.java`:逐行理解每条配置的意图
2. 用 Postman / 浏览器测试:不登录直接访问 `/api/students`,观察 401 响应
3. 去掉 `@Bean` 的 SecurityFilterChain对比默认行为表单登录页
4. 尝试把 `sessionCreationPolicy` 改回 `IF_REQUIRED`,观察 JSESSIONID 的出现
**核心文件**
- `config/SecurityConfig.java` — 安全配置入口
- `service/AuthService.java` — 认证逻辑login / register
**思考题**:为什么要用 `requestMatchers(...).permitAll()` 放行 `/api/auth/**`?不放行会怎样?
---
## Day 2JWT 认证 —— 登录 / 注册
**知识点**
- JWTJSON Web Token结构Header.Payload.Signature
- 对称密钥签名HMAC-SHA256
- JJWT 库的使用:`Jwts.builder()` 生成,`Jwts.parser().verifyWith()` 验证
- Claimssubject、issuedAt、expiration、自定义 claimrole
- 前端 Token 存储localStorage vs sessionStorage vs Cookie 的权衡
- OncePerRequestFilter为什么用它而不是普通 Filter
**动手 → 理解**
1. 用浏览器 DevTools Application 面板查看 localStorage 中的 `wk5_token`
2. 把 token 粘贴到 [jwt.io](https://jwt.io) 在线解析,观察 payload 内容
3. 修改 `application.yml``jwt.expiration` 为 600001分钟等一分钟再刷新页面
4. 手动篡改 localStorage 中 token 的某一个字符,观察 401 错误
**核心文件**
- `security/JwtUtil.java` — Token 生成与解析工具
- `security/JwtAuthFilter.java` — 过滤器:从请求头提取并验证 Token
- `config/DataInitializer.java` — 启动时创建默认用户
**思考题**JWT 的三个部分中,哪个是"不可伪造"的关键?为什么?
---
## Day 3RBAC 方法级安全 —— 权限控制
**知识点**
- RBACRole-Based Access Control角色 → 权限 → 资源
- `@EnableMethodSecurity` 开启方法级注解
- `@PreAuthorize("hasRole('ADMIN')")` 的 SpEL 表达式
- SecurityContextHolder如何在 Controller 中获取当前用户
- `Principal` 参数注入
- 权限不足时的 AccessDeniedException → 全局异常处理返回 403
**动手 → 理解**
1. 用 user/123456 登录,尝试新增学生,观察后台日志中的 AccessDeniedException
2. 在前端查看USER 角色的"新增"按钮为何消失app.js 中的 `checkRoleUI()`
3.`StudentController.java``deleteStudent` 方法上暂时注释掉 `@PreAuthorize`,用 USER 登录后通过 fetch 发送 DELETE 请求验证 SecurityConfig 的 URL 级别拦截
4. 登录 admin 后访问 `/api/students/me`,查看返回的 Principal 信息
**核心文件**
- `controller/StudentController.java``@PreAuthorize` 注解
- `exception/GlobalExceptionHandler.java` → AccessDeniedException 处理
**思考题**URL 级别拦截SecurityConfig和方法级拦截@PreAuthorize)各有什么适用场景?
---
## Day 4Redis 缓存集成
**知识点**
- Spring Cache 抽象:`@EnableCaching``@Cacheable``@CacheEvict`
- Redis 作为缓存后端 vs 默认 ConcurrentHashMap
- 缓存 Key 的 SpEL 表达式:`#id``'list'`
- `@CacheEvict(allEntries = true)` 的使用场景和风险
- application.yml 中的 Redis 连接配置
- Redis 命令行基本操作:`KEYS *``GET key``DEL key`
**动手 → 理解**
1. 启动项目,访问 `/api/students` 两次,观察第二次的响应速度变化
2.`redis-cli KEYS *` 查看 Spring Cache 生成的 Key 格式
3.`redis-cli GET "students::list"` 查看缓存的序列化数据
4. 新增一个学生后,再次 `KEYS *` 观察缓存是否被清空
5. 暂时关掉 Redis 服务,观察应用是否还能正常启动(`spring.cache.type: none`
**核心文件**
- `service/jpa/StudentJpaService.java` — JPA 服务的缓存注解
- `service/mp/StudentMpService.java` — MP 服务的缓存注解
- `application.yml` — Redis 与 Cache 配置
**思考题**:为什么新增/修改/删除时要 `allEntries = true` 清理全部缓存,而不是只清理单条?
---
## Day 5Actuator 应用监控
**知识点**
- Spring Boot Actuator 的端点Endpointshealth、info、beans、env、mappings
- `/actuator/health`应用健康状态UP / DOWN
- `/actuator/beans`Spring 容器中所有 Bean 的列表和依赖关系
- `/actuator/env`:运行时环境变量和配置属性
- `/actuator/mappings`:所有 URL 映射Controller + Filter
- 生产环境中端点的安全控制
**动手 → 理解**
1. 浏览器访问 `http://localhost:8080/actuator` 查看所有可用端点
2. 访问 `/actuator/health`,观察 JSON 中的 `status: "UP"`
3. 访问 `/actuator/beans`,搜索 "jwt" 或 "security" 找到相关 Bean
4. 访问 `/actuator/mappings`,找到自己写的 StudentController 的映射信息
5.`application.yml` 中把 `include` 改为只保留 `health,info`,重启再访问 `/actuator`
**核心文件**
- `application.yml``management.endpoints.web.exposure.include`
- `pom.xml``spring-boot-starter-actuator`
**思考题**:为什么不应该在生产环境暴露 `/actuator/env`?里面会有什么敏感信息?
---
## Day 6自定义校验注解 —— @ValidScore
**知识点**
- Bean ValidationJSR 380的扩展机制
- `@Constraint(validatedBy = ...)` 元注解
- `ConstraintValidator<A, T>` 接口:`initialize()` + `isValid()`
- 自定义注解的三要素:`message``groups``payload`
- `ConstraintValidatorContext` 自定义错误消息
- `@ValidScore` + `@NotNull` 的配合使用
**动手 → 理解**
1. 阅读 `ScoreValidator.java``isValid` 方法,理解 null 的处理逻辑
2. 在前端新增学生时,输入成绩 150 并提交,观察后端返回的校验错误
3. 修改 `@ValidScore``message` 默认值为中文提示,观察返回的 JSON
4. 尝试把 `@ValidScore` 应用到 `Student` 实体的 `score` 字段DTO 校验 vs 实体校验的区别)
5. 动手写一个新的自定义注解 `@ValidEmail`,校验邮箱格式
**核心文件**
- `annotation/ValidScore.java` — 自定义注解定义
- `annotation/ScoreValidator.java` — 校验逻辑实现
**思考题**`isValid` 中对 null 返回 true而不做 null 判断。如果调用方忘记加 `@NotNull`,会发生什么?
---
## Day 7MyBatis-Plus 进阶 —— 逻辑删除 & LambdaQueryWrapper
**知识点**
- 逻辑删除 vs 物理删除:概念差异与适用场景
- `@TableLogic`:标记逻辑删除字段
- `mybatis-plus.global-config.db-config.logic-delete-field` 全局配置
- 逻辑删除的 SQL 行为DELETE → UPDATE SET deleted=1SELECT 自动追加 deleted=0
- JPA 如何手动实现逻辑删除(@Query 中添加 `s.deleted = 0`
- LambdaQueryWrapper 条件构造:`.eq()``.like()``.orderByDesc()``.between()`
- 分页拦截器 PaginationInnerInterceptor 的 `overflow``maxLimit` 配置
**动手 → 理解**
1. 用 XML (MyBatisPlusConfig) 配置逻辑删除 vs 用 `@TableLogic` 注解的效果对比
2. 在 MySQL 中执行 `SELECT * FROM student WHERE deleted = 1`,验证逻辑删除后的数据是否存在
3. 对比 MP 服务(自动过滤 deleted和 JPA 服务(手动写 deleted=0 条件)的实现差异
4. 尝试修改 `maxLimit` 为 5观察分页查询被限制的效果
5. 在 LambdaQueryWrapper 上尝试组合条件:`like(Student::getName, keyword).between(Student::getAge, 18, 25)`
**核心文件**
- `config/MyBatisPlusConfig.java` — 分页拦截器 + 逻辑删除全局配置
- `service/mp/StudentMpService.java` — MP 服务的 LambdaQueryWrapper 使用
- `service/jpa/StudentJpaService.java` — JPA 手动逻辑删除的 @Query 写法
- `application.yml``mybatis-plus.global-config.db-config`
**思考题**:逻辑删除和物理删除各适用于什么业务场景?为什么 Week 5 选择了逻辑删除?
---
## Week 5 总结
| 维度 | Week 4v1 | Week 5v2 |
|------|-------------|-------------|
| 认证 | 固定 Token 字符串 | JWT 动态签发 + 过期控制 |
| 授权 | 无角色概念 | RBACADMIN / USER |
| 安全框架 | 手写 Interceptor | Spring Security + FilterChain |
| 缓存 | 无 | Redis + Spring Cache 注解 |
| 监控 | 无 | Actuator 端点 |
| 校验 | @Min/@Max 内置注解 | 自定义 @ValidScore |
| 数据库 | MP/JPA 直接操作 | MP 逻辑删除 + JPA 手动兼容 |
**环境保障**
- MySQL 8.0+,数据库 `gc_plan`,表 `student` + `users`
- Redis 需运行在 localhost:6379默认端口
- `DataInitializer` 在首次启动时自动创建 admin/123456 和 user/123456
- 如需重置:删除 users 表中记录,重启应用即可重新初始化
**附加练习**
1. 给 USER 角色新增一个权限:可以查看但不能修改自己的详细信息
2. 用 Redis 缓存 JWT Token实现"黑名单"注销功能
3. 在 Actuator 中增加自定义 HealthIndicator检查 Redis 连接状态)
4.@ValidScore 扩展为支持不同学科的不同分数范围