功能:实现短信验证登录
问题:在多台 Tomcat
服务器的集群环境下,如果没有进行 Session
共享配置,用户在请求被路由到不同的服务器时,Session
数据就会丢失。默认情况下,Tomcat
的 Session
是存储在服务器的内存中的,不会在不同的服务器间自动共享。
解决:Redis 是一个内存数据存储,可以用来存储 Session
数据。
特点:符合session的key-value形式
位于tomcat以外的一种存储形式,任何tomcat都能访问
内存存储
前台页面:
输入手机号,发送验证码请求
在接口的service中先正则校验手机号的格式-----》通过随机数工具生成六位验证码---》通过redis方法以手机号为Key将验证码存储在redis,并设置一个过期时间。
过期时间:当过期后,Key-Value将从数据库中自动删除
@Override
public Result sendCode(String phone, HttpSession session) {
//校验手机号
if (RegexUtils.isPhoneInvalid(phone)){
return Result.fail("phone err");
}
//生成验证码
String code = RandomUtil.randomNumbers(6);
//保存到redis
stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY+phone,code,RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
//发送模拟
log.info(code);
验证码登录和注册
将控制台中返回的验证码输入到输入框,发起登录
依旧校验一下手机号--》通过之前设置的Key从数据库获取保存的验证码,判断是否一样--》根据号码信息从mysql中查询用户信息,判断用户是否存在--》不存在则创建
query().eq("phone", phone).one():MP方法,根据号码返回一条信息
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//校验手机
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)){
return Result.fail("phone err");
}
//校验验证码,从redis获取
String code = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
if (code==null||!code.equals(loginForm.getCode())){
Result.fail("code err");
}
//没问题,查用户
User user = query().eq("phone", phone).one();
//不存在则创建
if (user==null){
user=createUser(phone);
}
private User createUser(String phone){
User user = new User();
user.setPhone(phone);
user.setNickName("user_"+RandomUtil.randomString(6));
save(user);
return user;
}
--》将创建的用户存储到redis
由于user有密码,电话等敏感信息,所以只需要存储几个普通信息就行了,准备一个实体类并转换一下
@Data
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
我们需要将这个对象以Hash(键值对的集合)的结构存储到redis。由于stringRedisTemplate处理的是字符串类型,然后id是Long类型的,所以以下会报错:类型转换错误
putAll:一次性将Map中的键值对存入redis
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> stringObjectMap1 = BeanUtil.beanToMap(userDTO);
stringRedisTemplate.opsForHash().putAll(stringObjectMap1);
我们就自己手动写一个Map,键值都是String类型的
valueOf: 更加安全,因为它可以处理 null
值。如果对象是 null
,它会返回 "null"
字符串,而不是抛出 NullPointerException
。toString处理非NULL
存入的Key要随机生成,这样比较安全一些,如果直接写号码,容易轻易被获取保存。
//生成一个token作为key值,
String token = UUID.randomUUID().toString(true);
Map<String, String> stringObjectMap = new HashMap<>();
stringObjectMap.put("id",String.valueOf(userDTO.getId()));
stringObjectMap.put("nickName",String.valueOf(userDTO.getNickName()));
stringObjectMap.put("icon",String.valueOf(userDTO.getIcon()));
存入
stringRedisTemplate.opsForHash().putAll("login:token"+token,stringObjectMap);
同样也要给这个token设置有效期,最后返回给前端
stringRedisTemplate.expire("login:token"+token,30,TimeUnit.MINUTES);
return Result.ok(token);
查看
docker容器化mysqlhttps://mp.csdn.net/mp_blog/creation/editor/142057264
AOP切面应用https://mp.csdn.net/mp_blog/creation/editor/141781810