@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(loginForm.getPhone()))
return Result.fail("手机号格式错误");
// 2.校验验证码,从session中获取
//2.1 改进 从redis中获取验证码并校验
String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.equals(code)) {
// 不一致,报错
return Result.fail("验证码错误");
}
//4一致,根据手机号查询用户, 并且不抛出异常 false
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getPhone, loginForm.getPhone());
User user = this.getOne(wrapper, false);
//5.判断用户是否存在,手机号注册
if (user == null) {
// 6没有查询到用户,则注册新用户
user = createUserWithPhone(loginForm.getPhone());
}
// TODO 新增 6.生成JWT
Map<String, Object> claims = new HashMap<>();
// 用户ID
claims.put(JwtClaimsConstant.USER_ID, user.getId());
// 确保密钥是 byte[] 类型
String secretKey = jwtProperties.getUserSecretKey();
byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8); // 推荐指定编码
// 生成 Token
String jwttoken = JWTUtil.createToken(claims, keyBytes);
// 7.2.将User对象转为HashMap存储
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), //beanToMap方法执行了对象到Map的转换
CopyOptions.create()
.setIgnoreNullValue(true) //BeanUtil在转换过程中忽略所有null值的属性
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())); //对于每个字段值,它简单地调用toString()方法,将字段值转换为字符串。
// 7.3.存储
String tokenKey = LOGIN_USER_KEY + userDTO.getId();
// 7.4.将jwttoken存入userMap中
userMap.put("jwttoken",jwttoken);
stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
// 7.4.设置token有效期
stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 8.返回token
return Result.ok(jwttoken);
}
private User createUserWithPhone(String phone) {
// 1.创建用户对象
User user = new User();
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
user.setPhone(phone);
//2.保存用户
save(user);
return user;
}
可以再设置一个refresh_token,该token过期时间较长,仅用来获取新token,不做其他用途。
首次请求返回token和refresh_token,当请求反馈状态是token过期时,使用refresh_token请求新的token和refresh_token,然后继续未完成的请求。
当refresh_token过期时,则重新登录。