黑马点评学习笔记:短信登录流程、ThreadLocal 隔离与 Redis 共享 Session
短信登录
1. 基于 Session 实现短信登录
完整流程:

接口:


拦截器拦截到用户信息后保存到 ThreadLocal 中。因为ThreadLocal 它是一个线程域对象,每一个进如 Tomcat 的请求都是一个独立的线程,将来 ThreadLocal 就会在线程中开辟一个内存的空间去保存对应的用户,这样一来每个用户互相不干扰。
为什么要使用拦截器校验登录状态?
好处:
统一管理:所有登录检查在一个地方完成
代码复用:不用在每个Controller方法里都写检查逻辑
职责清晰:业务代码只关心业务,不关心权限检查
易于维护:修改登录规则只需改一处
为什么要用 ThreadLocal 保存用户信息?
在Web服务器中的问题:
Tomcat服务器有线程池,多个请求可能复用同一个线程
如果用普通变量存储用户信息,会发生数据混乱
A用户的信息可能被B用户看到(严重的安全问题!)
注意: 使用 ThreadLocal 必须清理,如果不清理,线程被放回线程池后,下一个请求可能会拿到上一个用户的信息,造成严重的隐私泄露!
💻 ThreadLocal 的真实存储结构
服务器上有多个线程(处理多个用户请求):
线程1 (处理用户A的请求)
└─ ThreadLocalMap (线程私有的)
└─ key: ThreadLocal对象
└─ value: UserDTO(id=1, nickName="张三", icon="xxx")
线程2 (处理用户B的请求)
└─ ThreadLocalMap (线程私有的)
└─ key: ThreadLocal对象
└─ value: UserDTO(id=2, nickName="李四", icon="yyy")
线程3 (处理用户C的请求)
└─ ThreadLocalMap (线程私有的)
└─ key: ThreadLocal对象
└─ value: UserDTO(id=3, nickName="王五", icon="zzz")
关键点:
每个线程都有自己的 ThreadLocalMap - 不是共享一个大 Map
ThreadLocalMap 是 Thread 类的一个成员变量 - 属于线程私有
不同线程之间互不干扰 - 线程1拿不到线程2的数据
使用 UserDTO 类隐藏敏感信息。
2. 基于 Redis 实现共享 Session 登录
session 共享问题
多台 Tomcat 并不共享 session 存储的空间,当请求切换到不同的 Tomcat 服务时导致数据丢失的问题。
Session 的替代方案应该满足:
- 数据共享
- 内存存储
- key、value 结构



对于验证码存储的 key 选择:
以手机号为 key 存储验证码
以手机号为 key 读取验证码
对于保存用户的 key 选择:
以随机的 token 为 key 存储用户数据
有两种选择:
第一:使用 String 结构,以 JSON 字符串来保存,优点比较直观
第二:使用 Hash 结构,将对象中的每个字段独立存储,可以针对单个字段做 CRUD,优点内存占用更少(本项目的选择方案)
对于前端代码对 token 的保存和访问携带的实现代码:


随机 Token 不可预测、更安全、更灵活
部分代码:
//TODO 这里的userDTO 里面的id 是Long类型,存入Redis中需要转换为String类型
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
//对于每个字段,不管它原来是什么类型,都给我转成字符串
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
登录拦截器优化

更多推荐



所有评论(0)