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

10 KiB
Raw Blame History

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 在线解析,观察 payload 内容
  3. 修改 application.ymljwt.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.javadeleteStudent 方法上暂时注释掉 @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 keyDEL 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/beansSpring 容器中所有 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.ymlmanagement.endpoints.web.exposure.include
  • pom.xmlspring-boot-starter-actuator

思考题:为什么不应该在生产环境暴露 /actuator/env?里面会有什么敏感信息?


Day 6自定义校验注解 —— @ValidScore

知识点

  • Bean ValidationJSR 380的扩展机制
  • @Constraint(validatedBy = ...) 元注解
  • ConstraintValidator<A, T> 接口:initialize() + isValid()
  • 自定义注解的三要素:messagegroupspayload
  • ConstraintValidatorContext 自定义错误消息
  • @ValidScore + @NotNull 的配合使用

动手 → 理解

  1. 阅读 ScoreValidator.javaisValid 方法,理解 null 的处理逻辑
  2. 在前端新增学生时,输入成绩 150 并提交,观察后端返回的校验错误
  3. 修改 @ValidScoremessage 默认值为中文提示,观察返回的 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 的 overflowmaxLimit 配置

动手 → 理解

  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.ymlmybatis-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 扩展为支持不同学科的不同分数范围