Files
gc-plan/week5/教案.md
2026-04-29 23:45:17 +08:00

217 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 扩展为支持不同学科的不同分数范围