当前位置 : 主页 > 编程语言 > 其它开发 >

redis 实践(一)实现登录

来源:互联网 收集:自由互联 发布时间:2022-07-22
redis 实践(一)实现登录 这段代码值得借鉴,搬完就是我的。 背景 ​有些项目登录的逻辑都是如上图所示,但是实际访问量上来了,不可避免需要tomcat集群,但是多台Tomcat并不共享
redis 实践(一)实现登录

这段代码值得借鉴,搬完就是我的。

背景

image-20220720230543474

​ 有些项目登录的逻辑都是如上图所示,但是实际访问量上来了,不可避免需要tomcat集群,但是多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务时会导致数据丢失的问题。

​ 所以需要数据共享内存存储key、value结构

基于Redis的登录流程

image-20220720232133982

登录代码实现 发送验证码
@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
        // set key value ex 120
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code, LOGIN_CODE_TTL,TimeUnit.MINUTES);
        //5. 模拟发送验证码
        log.debug("发送验证码成功,验证码为{}",code);
        //返回ok
        return Result.ok();
    }

登录的实现
  • 先检验验证码,如果验证码正确,则进行用户的校验,如果用户不存在,则进行添加数据库的操作。

     //1.检验手机号
            String phone = loginForm.getPhone();
            if(RegexUtils.isPhoneInvalid(phone)){
                return Result.fail("手机号错误");
            }
            //2.从Redis获取验证码并且检验验证码
            String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
            String code = loginForm.getCode();
            if(cacheCode == null || !cacheCode.equals(code)){
                return Result.fail("验证码错误");
            }
            //查询数据库
            User user = query().eq("phone",phone).one();
            //用户不存在就创建新用户并且保存到数据库
            if(user == null){
                //createUserWithPhone 创建用户的函数
                user = createUserWithPhone(phone);
            }
    
  • 然后把用户信息添加到redis,具体思路就是先根据自己的规则生成一个token,作为用户的凭证,这个凭证需要返回前端,并且需要和前端约定,每次的异步请求都需要携带这个token。这里向redis存放的键是这个token,值是用户的基本信息。

    //保存信息到redis中
            //随机生成token,作为登录令牌
            //hutool 中 的方法,为true就不带下划线
            String token = UUID.randomUUID().toString(true);
            //将User对象转为Hash存储,函数用法为user对象转成UserDTO(存在用户的基本信息)
            UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
            //hutool:将userDTO对象转成map
            //这个方法可以自定义key和value
            Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                    CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
            //存储
            String tokenKey = LOGIN_USER_KEY + token;
            stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
            //设置有效期
            stringRedisTemplate.expire(LOGIN_USER_KEY + token, 30, TimeUnit.MINUTES);
            return Result.ok(token);
    

    image-20220720234510168

基于Redis的登录校验 实现思路

登录校验这里交给拦截器,但是这里需要考虑Redis的token的过期问题,每次的异步请求,都需要进行一次token过期时间的刷新。这里可以用两个拦截器。

image-20220720235557876

实现代码
  • 第一个拦截器,先获取请求头里面的token,再将用户信息放到ThreadLocal里面

    //1. 获取请求头中的token
            String token = request.getHeader("authorization");
    
            if (StrUtil.isBlank(token)) {
                //未登录,也不进行拦截
               // response.setStatus(401);
                return true;
            }
            //2. 基于token获取redis中的用户
            Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
                    .entries(RedisConstants.LOGIN_USER_KEY + token);
            //3. 判断用户是否存在
            if(userMap.isEmpty()){
                //4. 不存在,不拦截,不返回 401 状态码
                //response.setStatus(401);
                return true;
            }
            // 5. 将查询到的Hash数据转为UserDTO对象
            UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
            // 6. 存在,保存用户信息到ThreadLocal
            UserHolder.saveUser(userDTO);
            // 刷新token
            stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
    
            return true;
    
  • 第二个拦截器

     //判断是否需要拦截(ThreadLocal中是否有该用户)
            if(UserHolder.getUser() == null){
                //没有,拦截
                response.setStatus(401);
                //拦截
                return false;
            }
    
            //有用户,放行
            return true;
    
上一篇:win10安装nvm
下一篇:没有了
网友评论