1.easypan-登录注册
1.0 遇到的问题
1.0.1 JRebel 导致的 Bug
场景:最开始 yml 中数据库的信息配置错了,修改对后从新构建项目运行(使用了 Jrebel),没有重启,导致一直错误
原因:JRebel 热加载插件缓存了旧的数据库配置…,他不会热加载 yml 配置文件
1.0.2 APIFox 插件导致请求失败
场景:更改了请求配置,原本是@RequestMapping,请求失败,改为争取的@GetMapping,还是请求失败
原因:更改后没有刷新 APIFox 插件,导致还是原本的请求
1.1 图形验证码
1.1.1 当前方案
-
自写 创建验证码图像类
-
前端 api 增加 time 字段保证每次请求都不同,避免查询缓存,后端不用处理 time 字段
-
设置响应头禁止缓存该 HTTP 请求
-
这三个响应头共同构成了防御性缓存策略,确保:
-
用户不会看到缓存的旧验证码
-
代理服务器不会返回历史数据
-
浏览器每次都必须从服务器获取新验证码
-
Tomcat 默认是 no-cache 的,设置上更保险
-
-
后端将验证码文本存储到 Session 中
- 根据传入的 type 判断是 0-登录注册,1-邮箱验证码发送
-
响应类型设为
image/jpeg
(存储在响应头中) -
将验证码存入该 Session 中
-
通过
type
参数支持存储到不同 Session 属性(如普通验证码和邮箱验证码分开存储) -
这样客户端只能看到图片,无法直接获取或修改验证码内容
-
yml 中设置
session:timeout: PT60M
会话 60 分钟后过期 -
Session ID 通过 Set-Cookie 响应头自动返回
-
默认 Cookie 名称为 JSESSIONID(Tomcat)或 SESSION(Spring Session)
-
浏览器后续请求会自动携带该 Cookie
-
Session 由 Tomcat 自行管理,不显示禁用 session 的话 Tomcat 还会创建 Session 到内存中,通过 JESSIONID Cookie 维护会话
-
-
将二进制图形验证码流写入响应体中返回给前端
- 之前写的 API 都是以 RESTAPI 的格式返回 JSON 数据,这里直接输出响应,Tomcat 将内存中的二进制流(封装后的响应报文)通过 Socket 发送
验证码为什么采用这种方式?
-
性能考虑:二进制流直接输出效率更高
-
资源管理:避免图片数据在内存中多次转换
-
协议要求:图片响应需要精确控制 Content-Type 等头信息
-
框架限制:Spring 的消息转换器主要针对结构化数据设计
1.1.2 其他方案
-
使用第三方库:Kaptcha 库,简单易用,功能丰富,支持多种干扰效果
-
基于 SVG 实现,矢量图形不失真,文件体积小
-
行为验证码(如滑动拼图)
-
Redis 存储方案-支持分布式
1.2 邮箱验证码
1.2.1 当前方案
-
前端传入 email 和图形验证码,以及 type:0-注册,1-找回密码
-
获取 session 中存储的 code 做比较,没问题之后发送验证码
-
先判断是否为注册,校验邮箱是否已经注册
-
生成验证码并发送给用户短信,使用 spring 自带的 mail 相关工具包封装邮件
-
封装信息:邮箱相关信息,这里短信内容默认配置存入 redis,封装了 RedisUtil 常用方法,Redis 的相关操作放入了 RedisComponent 中
-
发送邮件后先将旧的验证么标识为已用,再封装将新验证码信息存入数据库
1.2.2 可优化的点
-
增加 IP 限制,防刷
-
增加邮箱发送频率限制
-
异步发送邮件,避免同步逻辑阻塞
-
邮件发送失败重试机制
-
数据库存储改为 redis 存储验证码
-
分布式锁防止并发(多次重复请求)
-
运维方面,日志、限流控制
1.2.3 其他方案
-
第三方验证码服务
-
自建验证码服务
-
基于 Spring Cloud 的微服务架构
-
基于消息队列的异步处理
-
基于分布式缓存的存储方案
-
-
redis+token
1.2.4 MP 查询方案
引入 Mybatis-Plus 后,IService 内部会调用 BaseMapper 类,功能更强大,在服务层获取数据库信息的方案有
-
查询本表的方案:
-
继承 BaseMapper 方法
-
Lambda 表达式方式 (推荐),使用 IService 中的 lambda 方法
-
链式调用方式
-
-
查询其他表的方案:
-
通过注入的 Mapper 查询 (当前代码方式)
-
自定义 Mapper 方法 (需在 Mapper 中定义)
-
通过 Service 调用
-
使用 Db 静态工具类,减少循环依赖注入的风险(推荐)
-
1.3 AOP 实现参数校验
1.3.1 实现步骤
-
引入 AOP 依赖
-
定义自定义注解(可选)
-
创建切面类并注册为 Bean
-
使用@Pointcut 定义切点
-
编写@Before,@After,@Around 等通知方法
-
获取方法和参数信息
-
实现业务逻辑(如数据库字段内容添加、参数校验)
-
统一异常处理与日志记录
-
-
在目标方法上使用注解
1.3.2 对比其他项目
苍穹外卖、黑马点评中是使用 MVC 的 Interceptor 统一拦截请求然后校验的
这里 AOP 自己实现
-
更细粒度的控制(方法级别)
-
可以结合自定义注解灵活控制
-
不局限于 Web 层,可应用于 Service 层
对比建议:
-
对于纯 Web 请求的登录校验,推荐使用拦截器方案,因为:
-
更符合 MVC 架构思想
-
性能更好(AOP 会有额外代理开销)
-
更易获取 Web 相关对象
-
-
考虑 AOP 的场景:
-
需要方法级别的细粒度控制
-
校验逻辑需要复用至非 Web 组件
-
已有基于注解的权限体系
-
1.4 登录注册&CRUD
1.4.1 登录逻辑
-
账号密码登录,先验证图形码
-
再验证账号密码,“记住我”1 前端存储的是 md5 加密后的密码
-
更新登录时间
-
返回给前端基础的用户信息
1.4.2 注册逻辑
-
校验图形验证码
-
验证邮箱、昵称是否存在
-
验证邮箱验证码
-
初始化用户云盘空间信息
-
保存用户信息到数据库
1.4.3 头像获取
-
直接是存储二进制文件到本地
-
先根据用户 id 拼接图像名称查找
-
没有则使用默认头像
1.4.4 其他
密码更新、重置
退出登录
头像更新 - 直接创建新的头像文件
1.4.5 存在问题
-
MD5 安全性不足-改用 BCrypt
-
Session 存储方式存在会话劫持风险
-
缺少登录失败次数限制
-
缺少密码强度校验
1.4.6 其他方案
-
JWT + Redis 方案
-
OAuth2.0 方案:支持第三方登录
-
Spring Security:安全性高
1.5 登录后默认行为
1.5.1 获取用户默认信息
因为这里采用的是 session 认证,要根据 session 去内存中获取用户信息,所以单独封装了 ABaseController 里面封装了获取用户信息的方法,方便多个接口调用获取。
而苍穹外卖是 JWT+ThreadLocal 方案则是登录后返回给前端 JWT,以后请求先拦截器拦截请求获取 JWT 然后解析出用户信息(包含唯一标识)然后存入 ThreadLocal 中,方便后续直接使用。
而黑马点评是在登录后生产随机 token 返回给前端,并将用户信息作为 value,token 作为小 key 存入 redis 的 hash 中,以后请求携带 token,拦截然后去 redis 中做校验,将用户信息存入 ThreadLocal 中
这里获取用户默认信息以及从 redis 中获取用户的空间使用信息,如果 redis 中没有则先查数据库再缓存并返回
1.5.2 获取文件列表
-
先获取具体的文件类型:前端传来的与数据库存储的有个映射
-
然后根据 session 分页查询用户文件信息