Redis在登录接口中实现token时间的自适应增长

本篇文章前置知识,jwt令牌有一定了解,对spring有一定了解,对登录接口的实现有一定了解。

Redis在登录接口中的应用

在我们平时写登录接口的时候,大家可能都会习惯性的说上一句,很简单哇。不就是前端像后端发送一个账号密码的请求,然后后端验证请求后,返回一个jwt令牌给前端,最后给令牌设置一个3天的过期时间,这样不就写完了吗。

但是大家可能忽略了一个我们平时很常见的问题, 就是在上述操作中,我们jwt的令牌过期时间是写死了的。想象一个场景,我们在一次oj竞赛网站中,竞赛时间要到了,我们点击提交,结果jwt的令牌时间刚好结束了,那我们不得骂死这个开发哇。

所以为了解决上述的一个问题,这里我们需要用其他组件来对返回的Token进行一个存储和叫校验,这个组件必须得满足,有存储功能,可以设置过期时间,同时因为会反复查询,所以必须要速度快。 Redis当然就是最佳人选啦。

这里讲一下大概得流程

开发思路

我们在后端生成jwt令牌的时候,将userId作为jwt中的存储内容进行传入,生成jwt令牌,同时将userId作为key, 敏感信息作为value 传入进入Redis数据库中,同时设置过期时间,要是用户调用接口的时候在Redis中进行一个查询,看是否能通过userId也就是Redis中的key来查到相应value,要是能查到,则表示当前用户为登录状态,同时在每一次通过查询之后,对接口进行一个剩余时间(redis中ttl)的判断,要是剩余时间不长,则对Redis中的数据进行延长,以达到一个重置登录状态的效果

这里我们的开发环节就写部分伪代码。

创建生成jwt令牌的工具类

这里就当大家对jwt有一定了解了, 因为jwt中payload 部分是可以被解码的。所以我们不能用jwt进行敏感信息的存储,但是我们可以用jwt来存储用户的身份标识。我们首先进行jwt令牌的创建其中claims就是jwt可以存储的对象,(网上可以搜到常用工具)

java 复制代码
 /**
     * ⽣成令牌
     *
     * @param claims 数据
     * @param secret 密钥
     * @return 令牌
     */
    public static String createToken(Map<String, Object> claims, String secret)
    {
        //点进去HS512下面的算法全是非对称加密
        String token =
                Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512,
                        secret).compact();
        return token;
    }
    /**
     * 从令牌中获取数据
     *
     * @param token 令牌
     * @param secret 密钥
     * @return 数据
     */
    public static Claims parseToken(String token, String secret) {
        return
                Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

有了创建jwt的方法

我们假设后端已经完成了 验证用户账号密码的一个校验

此时调用createToken方法我们就可以进行Token的创建了 这里的Login对象为存储的敏感对象,我们可以自定义里面的字段

java 复制代码
   public String createToken(Long userId, String secret){
        //创建jwt令牌, 令牌中存储userId
        Map<String, Object> claim = new HashMap<>();
        claim.put("userId", userId);
        String token = JwtUtils.createToken(claim, secret);

        //这里的Login对象为自定义对象,可以存储用户的敏感信息,比如用户身份之类的
        Login login = new Login();
        login.setSusceptible(1);
        //最后2个参数为自己设置这里是伪代码
        redisTemplate.opsForValue().set(userId, login, timeout, timeUnit);
        return token;
    }

完成上述步骤基本的开发工作其实已经完成了,此时我们每一次前端发来请求之后我们就可以进行验证啦,当时要是项目是分布式项目的话,可以在gateway网关中完成拦截,单体架构可以就使用spring自带的拦截器就好了

java 复制代码
@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //先获取请求参数
        String url = request.getRequestURI();
        // 跳过不需要验证的路径 登录 和 测试等等
        if (......) {
            return true;
        }
        //从http请求头中获取token 进行身份认证 这里getToken()为自定义的方法啊
        String token = request.getToken();
        //先判断是否有令牌
        if (StrUtil.isEmpty(token)) {
            throw new Exception("令牌不能为空")
        }
        //获取令牌中信息 解析     payload中信息
        Claims claims;
        claims = JwtUtils.parseToken(token, secret); //获取令牌中信息 解析     payload中信息
        //令牌解析失败,可能已经被篡改过了
        if (claims == null) {
            throw new Exception("令牌已过期或验证不正确!");
        }
        //解析成功之后
        String userId = JwtUtils.getUserKey(claims); //获取jwt中的key
        //redis中是否存在jwt来判断令牌是否过期

        boolean isLogin = redisService.hasKey(userId);
        //Redis中不存在前面存储的Login对象表示登录已经过期
        if (!isLogin) {
            throw new Exception("登录状态已过期");
        }
        //说明令牌正确 
        .......
        //这里可以对用户的敏感数据进行一些处理
        return true;
    }

当前前面只是完成了jwt令牌正确性和完整性的一个校验。在处理完令牌之后还有一步很重要的内容就是判断当前令牌剩余的时候,要是剩余的时间少于一个规定值,我们就对令牌进行延长,要是长期不操作,jwt令牌就会失效。

我们来完善延长方法

java 复制代码
private boolean exTime(HttpServletRequest request,String userid){
        //获取Token
        String token =
                request.getHeader(HttpConstants.AUTHENTICATION);

        //找一个临界值 当时间小于临界值的时候进行一个延长 这里设定剩余120分钟是进行延长
        Long expire = redisService.getExpire(userid, TimeUnit.MINUTES);
        //180为自己设定的一个时间 可以随便设置一个L行的数 要是小于这个时间就进行延长
        if(expire != null && expire < 180L){
            redisService.expire(userid, CacheConstants.REDIS_EXP, TimeUnit.MINUTES);
        }
        return true;
    }

经过这一系列流程,登录接口的扩展就大致开发完成啦

相关推荐
瓜牛_gn39 分钟前
mysql特性
数据库·mysql
奶糖趣多多2 小时前
Redis知识点
数据库·redis·缓存
CoderIsArt3 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
师太,答应老衲吧5 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Channing Lewis6 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
毕业设计制作和分享7 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil277 小时前
Redis - String 字符串
数据库·redis·缓存
Hsu_kk8 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境8 小时前
第02章 MySQL环境搭建
数据库·mysql
knight-n8 小时前
MYSQL库的操作
数据库·mysql