首先了解一下整个登录的流程。
配置文件设置之类的略。
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result sendCode(String phone, HttpSession session) {
//1. 校验手机号
if(RegexUtils.isPhoneInvalid(phone)){
//2. 不符合,返回错误信息
return Result.fail("手机号格式错误");
}
//3. 符合,生成验证码
String code = RandomUtil.randomNumbers(6);
//4. 保存验证码到redis
stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY+phone,code,RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
//5. 发送验证码
log.debug("发送短信验证码成功,验证码:{}",code);
//返回
return Result.ok();
}
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
String phone = loginForm.getPhone();
//1. 校验手机号
if(RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号格式错误");
}
//2. 校验验证码
String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY+phone);
String code = loginForm.getCode();
if(cacheCode == null || !cacheCode.equals(code)){
//3. 不一致,报错
return Result.fail("验证码错误");
}
//4. 一致,根据手机号查询用户
User user = this.lambdaQuery().eq(User::getPhone, phone).one();
//5. 判断用户是否存在
if (user == null) {
//6. 不存在,创建新用户并保存
user = createUserWithPhone(phone);
}
//7. 保存用户信息到redis
//7.1 随机生产token,作为登录令牌
String token = UUID.randomUUID().toString(true);
//7.2 将User对象转为Hash
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
CopyOptions.create().setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
//7.3 存储
String key = RedisConstants.LOGIN_USER_KEY+token;
stringRedisTemplate.opsForHash().putAll(key,userMap);
//7.4 设置30分钟有效期
stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL,TimeUnit.MINUTES);
//8. 返回token
return Result.ok(token);
}
//根据手机号创建用户
private User createUserWithPhone(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
this.save(user);
return user;
}
RefreshTokenInterceptor用来刷新user在redis中存在时间
LoginInterceptor用来拦截是否登录
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate))
.addPathPatterns("/**")
.order(0);
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/user/code","/user/login",
"/blog/hot",
"/shop/**",
"/shop-type/**",
"/upload/**",//TODO 开启拦截
"/voucher/**"
).order(1);
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(UserHolder.getUser()==null){
response.setStatus(401);
return false;
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
public class RefreshTokenInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate){
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. 获取请求头中的token
String token = request.getHeader("authorization");
//2. 基于token获取redis中用户
if(StrUtil.isBlank(token)){
return true;
}
String key = RedisConstants.LOGIN_USER_KEY + token;
Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
.entries(key);
//3. 判断用户是否存在
if(userMap.isEmpty()){
return true;
}
//5. 将查询到的hash数据转化为UserDto对象
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
//6. 存在,保存用户信息到ThreadLocal中
UserHolder.saveUser(BeanUtil.copyProperties(userDTO,UserDTO.class));
//7. 刷新token有效期
stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//8. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
注:在拦截器中用到了ThreadLocal,可以不用,其他板块用到user信息时从redis中取即可