简介:
在日常生活中我们登录/注册某些网站/APP是通常可以选择 密码登录和手机号登录。
为什么手机号发送后会有验证码返回呢?
网站如何识别我的验证码是否正确?
如果我的个人网站也想要实现短信登录功能,具体该如何实现?
下面我将从用户的视角带着大家一步一步的了解短信登录背后的一切细节。
具体操作
输入手机号
点击【获取验证码】
后台:
验证手机号
//Slfg4 日志注解
public Result sendCode(String phone, HttpSession session) {
//1:先验证手机号格式 不符合就返回错误信息
if(RegexUtils.isPhoneInvalid(phone)){
//2:如果不符合,返回错误信息
return Result.fail("手机号格式错误");
}
//4:生成验证码
String code = RandomUtil.randomNumbers(6);
//保存验证码到redis phone作为key 并且有一个公共前缀
stringRedisTemplate.opsForValue()
.set(RedisConstants.LOGIN_CODE_KEY +phone,code
,RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);
//6:发送验证码 此处是返回到前端
log.debug("验证码是{}",code);
return Result.ok();
}
- 符合格式 生成验证码 将其存在redis中并发送给前端
输入验证码
点击【注册并登录】
- 后台以手机号为key与redis中存储的验证码进行比对 不相同则返回 “验证码不正确”
- 相同则看是否被注册过 并生成token
- 如果没有注册则新建用户数据并插入新数据
- 将用户的信息和token存入redis的map中
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//获取手机号
String phone = loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
//2:如果不符合,返回错误信息
return Result.fail("手机号格式错误");
}
//从reids中拿出验证码
String code = stringRedisTemplate.opsForValue()
.get(RedisConstants.LOGIN_CODE_KEY + phone);
if(loginForm.getPhone().equals(code)){
//如果验证码错误直接返回false
return Result.fail("验证码不正确");
}
//如果正确 在确定手机号是否已经被注册过
User user = query().eq("phone", phone).one();
//生成token 用hutool工具类生成的uuid toString(true)可以把uuid中的-去掉
String token = UUID.randomUUID().toString(true);
if(user==null){
//没有注册过新建并插入新数据
user=CreateNewUser(phone);
}
//hutool工具类 Beanutil
UserDTO userDTO= BeanUtil.copyProperties(user, UserDTO.class);
//运用redis中的map数据结构存储userDto对象
Map<String,String> map=new HashMap<>();
map.put("id",userDTO.getId().toString());
map.put("nickName",userDTO.getNickName());
map.put("icon",userDTO.getIcon());
stringRedisTemplate.opsForHash()
.putAll(RedisConstants.LOGIN_USER_KEY+token,map);
//设置时间一般是30分钟不进行操作,就会失效
stringRedisTemplate
.expire(RedisConstants.LOGIN_USER_KEY+token,
RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
return Result.ok(token);
}
private User CreateNewUser(String phone) {
User user=new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(4));
save(user);
return user;
}
验证成功 开始登录 拦截器的实现
- 拦截器 一:更新token信息/放行空token 用token从redis里拿出map对象转化为UserDTO对象存入ThreadLocal中,通过所有拦截器后释放
- 拦截器二:判断ThreadLocal中有没有UserDTO对线,有则放行,没有拦截