💡 核心背景:为什么引入 Redis?
在单体架构中,我们通常使用 Tomcat 的 Session 来存储登录用户信息。但在集群部署下,多台 Tomcat 服务器之间的 Session 是不共享的,会导致用户在 A 服务器登录后,请求路由到 B 服务器时又变成未登录状态。
为了解决这个问题,我们引入了 Redis 作为统一的数据存储中间件,实现跨服务器的状态共享。
🔄 短信登录核心三大流程
整个基于 Redis 的短信登录系统可以拆解为三个连贯的动作:
1. 发送短信验证码流程
这是用户交互的第一步,重点在于临时数据的存储。
- 校验手机号: 接收前端传来的手机号,使用正则表达式验证格式是否合法。
- 生成验证码: 如果合法,利用工具类(如 Hutool 的
RandomUtil)生成一个 6 位随机数字验证码。 - 存入 Redis: 以手机号为标识(如
login:code:173xxx),将验证码作为 String 类型存入 Redis,并设置一个较短的过期时间(例如 2 分钟)。 - 发送短信: 调用第三方短信服务将验证码发送给用户(项目中通常通过控制台日志模拟发送成功)。
2. 验证码登录 / 注册流程
这是核心业务逻辑,重点在于长效 Token 的生成与数据结构的转换。
- 验证验证码: 接收用户提交的手机号和验证码,从 Redis 中查出对应的验证码进行比对。
- 查询用户: 如果验证码一致,根据手机号去 MySQL 数据库查询该用户是否存在。
- 自动注册: 如果用户不存在,则系统自动为其创建一个新用户记录,并保存到数据库中。
- 生成 Token: 生成一个随机的 UUID 作为 Token(令牌),这将替代传统 Session ID 的作用。
- 存入 Redis: 将查询到或新创建的用户信息转换为
UserDTO,然后转为 Map(需注意将所有字段转为 String 类型),以 Token 为 Key 存入 Redis 的 Hash 结构中,并设置一个较长的过期时间(如 30 分钟)。 - 返回 Token: 将生成的 Token 字符串返回给前端,前端后续的请求都会在 Header 中携带这个 Token。
3. 校验登录状态与会话保持 (双拦截器机制)
这部分解决了用户活跃状态下的“自动续期”问题。
- 全局刷新拦截器 (
RefreshTokenInterceptor): 拦截所有路径 (/**)。尝试从请求头获取 Token,去 Redis 中查询对应的用户信息。如果查到了,将用户存入ThreadLocal(方便后续业务使用),并刷新该 Token 在 Redis 中的有效期。 - 登录校验拦截器 (
LoginInterceptor): 拦截需要登录权限的特定路径。它的逻辑非常简单,只需要检查ThreadLocal中是否有用户即可。如果没有,直接拦截并返回 401 状态码;如果有,直接放行。
📝 重点难点复习提醒
- Redis 存储类型的选择: 验证码用
String(简单临时),用户信息用Hash(节约内存,支持单字段修改)。 - 序列化坑点: 使用
StringRedisTemplate存储 Hash 对象时,一定要记得将对象中的非字符串字段(如 Long 类型的 id)转换成 String,否则会报ClassCastException(就是你刚刚踩过并修复的那个坑)。 - ThreadLocal 内存泄漏: 拦截器处理完请求后,务必要在
afterCompletion方法中调用remove()清除当前线程的数据。