双token鉴权校验

双token鉴权校验

面试官:说说cookie、session、token、jwt是什么,单点登录怎么实现前端双token校验单token情况下,前端请求后端接口携带Token校验,但是token失效时间如果设置长了会有安全隐患,设置短了用户需要频繁刷新用户体验不好。解决办法:设置两个Token,也就是登录完接口初...

2024年4月21日
846字

前端双token校验

单token情况下,前端请求后端接口携带Token校验,但是token失效时间如果设置长了会有安全隐患,设置短了用户需要频繁刷新用户体验不好。

解决办法:

设置两个Token,也就是登录完接口初次返回的,AccessToken和RefreshToken:AccessToken是用于鉴定用户身份,请求每一个接口都需要携带AccessToken;RefreshToken为刷新前者的接口所携带的token,也就是当AccessToken失效时候,调用接口携带RefreshToken返回新的RefreshToken和新的AccessToken,然后自动将新的AccessToken放到接口中重新请求,整个流程为无感操作,提升用户体验。

上述所提到的刷新接口:/auth/refresh,无需经过网关

// token刷新锁
let isRefreshing = false;
// 重试队列
let requests = [],

在Access_Token过期后,可能会短时间发送很多个网络请求,这些网络请求拦截器全都会请求刷新Token,这样显然是不合理的。其实只要刷新一次就够了,所以给刷新Token这步操作加一个锁,这也就是为什么要设置isRefreshing的原因。

在请求刷新Token的响应时间内,可能会有多个网络请求到达,我们又暂时没办法处理这些网络请求,所以我们先把这些网络请求promise对象存储到队列requests里面,等到刷新好Token之后再依次处理这些promise对象。

service.interceptors.response.use(
  res => {
    //accesstoken超时
    if (res.data.result_status === 30002) {
      //记录当前res
      let response = res;
      if (!isRefreshing) {
        isRefreshing = true;
        return tokenRefreshAPI({
          token: JSON.parse(window.localStorage.token)
        }).then((res) => {
          //refreshToken没过期,刷新成功
          console.log('tokenRefreshAPI', res.data);
          if (res.data.result_status === 0) {
            console.log('token刷新成功', res.data);
            //重新设置token
            window.localStorage.setItem('token', JSON.stringify(res.data.token));
            //执行失效的函数
            requests.forEach((cb) => cb());
            requests = []; // 重新请求完清空
            return service(response.config);
          } else if (res.data.result_status === 30011) {
            console.log('refreshToken过期,请重新登录');
            window.localStorage.clear();
            router.push('/login');
          }
        }).finally(() => {
          isRefreshing = false;
        });
      } else {
        console.log('token过期,剩余请求存入队列', axiosConfig.url);
        // 返回未执行 resolve 的 Promise
        return new Promise(resolve => {
          // 用函数形式将 resolve 存入,等待刷新后再执行
          requests.push(() => {
            resolve(service(axiosConfig));
          });
        });
      }
    }
    return res;
  })

Token的失效时长设置和前端定时器刷新时长的设置:

  • AccessToken:建议设置为20分钟,以确保即使一次刷新失败,用户仍有足够时间进行重试
  • RefreshToken:建议设置为30天,以减少用户频繁重新认证的需求,同时保持适当的安全性
  • 前端刷新:每9分钟刷新一次访问令牌,以确保访问令牌始终有效

  • Clientid是必备的

  • reponse
{
    "access_token": "B5483047ABCA442FE7C4B5A45F1EBBCB",
    "token_type": "bearer",
    "refresh_token": "DFBEC5333D5CA349CB9AC88B895FDAC0",
    "expires_in": "71999",
    "scope": "all",
    "member_id": "45",
    "user_id": "1914461805866944000",
    "group_id": "g001",
    "user_name": "DF001605",
    "host": null,
    "hostIp": null,
    "grayHost": null,
    "member_name": "jackyun_dev",
    "upgradeHost": null,
    "isGray": "1",
    "pdaUpgradeHost": null,
    "jti": "edb4d5f7-6b21-4725-8e13-afe0a57796bb",
    "webSocket": null,
    "grayWebSocket": null,
    "company": "jackyun_dev",
    "mobile": "19862171378",
    "environment": "dev",
    "softVersion": "standard",
    "authFlag": null,
    "platCode": "JACKYUN",
    "clientId": "jackyun_web_browser",
    "risk": "0",
    "riskToken": null,
    "loginDevice": "web_browser",
    "fastLoginToken": null,
    "isBindWeixinMiniProgram": "0",
    "realName": "吴祖涛",
    "imgUrl": "",
    "jdHost": "",
    "networkIp": null,
    "isAdmin": null,
    "pddHost": "",
    "testSwitcher": null,
    "softwareCode": null,
    "groupUpgradeInfo": null,
    "tags": null,
    "clientSn": "TC6NROAC-LATW-BROSWER",
    "roles": "1",
    "apiHost": null,
    "spaceHost": null,
    "isGreyTestMember": null,
    "greyTestWebPath": null,
    "forceChangePassword": "0"
}

chatgpt info:

文章评论区

欢迎留言交流

未登录,请先注册或登录后发表评论。

Leave comment