黑马点评登录设计

Alt text

短信验证码登录

  1. 登录界面输入手机号,然后点获取验证码。此时后端首先校验手机号格式是否正确,接着生成一个验证码。把手机号和对应的验证码存入 Redis,设置过期时间为 2 分钟。
  2. 登录界面填写验证码,然后点击登录。此时后端对比传过来的手机号和验证码。如果一致,则进行下一步。
  3. 如果该用户不存在,则创建用户并且保存用户到数据库。
  4. 随机生成token,作为登录令牌;并将 User 对象转成 Hash,以 token 为 key,以 hash为 value,存入 Redis。

校验登录状态

  1. 前端携带的请求会带有 token,所有请求都会被RefreshTokenInterceptor拦截。如果没有token直接放行
  2. 首先获取请求头中的 token,然后基于token获取redis中存储的用户(一个 Map对象)。
  3. 如果用户存在,则将Map对象转换为UserDTO,并且把它存储在ThreadLocal中,并且刷新 token 的有效期。

第二个Interceptor:除了指定的(不需要用户登陆的)路径之外,其它请求路径都会被 LoginInterceptor 拦截,如果此时没有登录,(UserHolder里为空—),则无法处理请求。

UserHolder

每个 ThreadLocal 可以存放一个对象,同一个线程的所有ThreadLocal都存在该线程私有的ThreadLocalMap中。
通过ThreadLocal的 get, set, remove方法,我们可以充分利用该线程的用户信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
    public static void saveUser(UserDTO userDTO){
        tl.set(userDTO);
    }
    public static UserDTO getUser(){
        return tl.get();
    }
    public static void removeUser(){
        tl.remove();
    }
}

一个需要登录的请求线程完成工作之后,loginInterceptor里面定义的afterCompletion方法会把该用户从UserHolder中移除。