无感刷新-双token

1.为什么要做双token?

双token系统通常用于提高安全性和分离不同级别的权限。在这种系统中,通常会有两个token:

  • Access Token:这是用户直接使用来访问资源的token。它的有效期较短,一旦过期,用户需要重新认证来获取新的access token。这样做的好处是即使access token被泄露,由于其有效期短,攻击者利用它进行不当操作的时间窗口有限。

  • Refresh Token:Refresh token是用来在access token过期后重新获取新的access token的。它的有效期通常较长,甚至可以说是永久的。但是,refresh token通常不会直接发送给客户端,而是保存在服务器端。当需要刷新access token时,客户端通过提供refresh token来请求新的access token。

此外,使用双token的原因还包括:

  • 安全性:通过短期的access token和长期的refresh token,可以在不影响用户体验的情况下,减少安全风险。即使access token被盗,由于其有效期短,损害可以被控制在一定范围内。

  • 权限管理:双token系统可以更好地管理用户的权限。例如,在开放平台中,第三方应用可以通过refresh token来获取access token,而不需要知道用户的用户名和密码,这样既保证了用户信息的安全,又赋予了第三方应用一定的权限。

  • 用户体验:双token系统可以在不影响用户体验的前提下,实现后台的安全管理和策略调整。用户不需要频繁登录,同时也能保证系统的安全性。

  • 灵活性:在需要进行更细粒度的权限控制或者策略调整时,双token系统提供了更多的灵活性。例如,可以在不改变refresh token的情况下,调整access token的有效期或权限范围。

总之,双token系统是一种常见的安全设计模式,通过分离短期和长期凭证,以及用户直接使用的token和用于刷新的token,来提高系统的安全性和灵活性。这种设计特别适用于需要与第三方应用共享权限而又不泄露用户敏感信息的场景。

2.流程图

3.前端代码实现

const refreshToken = async () => {
    return await axios.get('/apis/bm-member-service-app/api/member/refresh_token', {
        params: {
            refreshToken: beimao_store.refreshToken
        }
    })
        .then(function (response) {

            const { code, msg, data } = response.data
            if (code != 0) {
                return Promise.reject(new Error(msg));
            }

            beimao_store.token = data.token;
            beimao_store.refreshToken = data.refreshToken;
            return Promise.resolve("成功");

        })
        .catch(function (error) {
            return Promise.reject(new Error(error));
        });
}

4.后端代码

4.1登录时,下发双token

public Map<String,String> login1(LoginInfo loginInfo) {

        //1.验证手机号
        if (! Validator.isMobile(loginInfo.getTel())){
            throw new BizException(615,"手机号不正确");
        }

        //根据手机号查询
        Member member = memberDao.selectByTel(loginInfo.getTel());

        if (ObjectUtil.isEmpty(member)){
            throw new BizException(612,"手机号或者密码错误");
        }

        //使用jwt,下发业务access_token
        Map<String, Object> map = new HashMap<String, Object>() {
            private static final long serialVersionUID = 1L;
            {
                put("id", member.getId());
                put("nickName",member.getNickName());
                put("exp", System.currentTimeMillis()/1000 + 10 * 60*6*2);//设置10秒
            }
        };
        String token = JWTUtil.createToken(map, key.getBytes());
        //下发刷新token
        Map<String, Object> map2 = new HashMap<String, Object>() {
            private static final long serialVersionUID = 1L;
            {
                put("id", member.getId());
                put("exp", System.currentTimeMillis()/1000 + 10 * 60*6*24*180);//设置1小时
            }
        };
        String refreshToken = JWTUtil.createToken(map2, key.getBytes());
        Map tokens=new HashMap<String,String>();
        tokens.put("token",token);
        tokens.put("refreshToken",refreshToken);

        return tokens;
    }

4.2业务刷新时,前端调用后端接口,实现无感刷新

  @GetMapping("/refreshtoken")
    public Map<String,String> refreshToken( String refreshToken) {
        return loginService.refreshToken(refreshToken);
    }
 public Map<String, String> refreshToken(String refreshToken) {

            if(ObjectUtil.isEmpty(refreshToken)){
                throw new BizException(461,"refreshToken必传");
            }
            boolean b = false;
            // 验证算法,JWTValidator包含过期的验证,验证比较全面
            try {
                JWTValidator.of(refreshToken).validateAlgorithm(JWTSignerUtil.hs256(key.getBytes())).validateDate();
                b = true;
            }catch (Exception ex){
                ex.printStackTrace();
            }
            if(!b){
                throw new BizException(462,"refreshToken不正确");
            }

            //能走到这说明refreshToken是我们之前下发的。

            JSONObject jsonObject = JSONUtil.toBean(JWTUtil.parseToken(refreshToken).getPayload().toString(), JSONObject.class);

           int id = Integer.parseInt(jsonObject.get("id").toString());


         Member member = memberDao.selectById(id);

            if (ObjectUtil.isEmpty(member)) {
                log.error("手机号未注册");
                throw new BizException(613, "手机号或密码错误");
            }
            Map <String, Object> map = new HashMap<String, Object>() {
                private static final long serialVersionUID = 1L;

                {
                    put("id", member.getId());
                    put("nickName", member.getNickName());
                    put("exp", System.currentTimeMillis()/1000 + 10 * 60*6*2); //access_token过期时间10s
                }
            };
            String token = JWTUtil.createToken(map, key.getBytes());

            Map<String, Object> map2 = new HashMap<String, Object>() {

                private static final long serialVersionUID = 1L;
                {
                    put("id", member.getId());
                    //过期时间一小时
                    put("exp", System.currentTimeMillis()/1000 + 10 * 60*6*24*180);//refreshToken过期时间1h
                }
            };

            //创建刷新令牌refreshToken
            String refreshToken2 = JWTUtil.createToken(map2, key.getBytes());
            // 创建返回的登录结果Map对象
            Map tokens = new HashMap<String, String>();
            tokens.put("token", token);
            tokens.put("refreshToken", refreshToken2);
            return tokens;

        }

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值