什么是JWT

JWT是全称是JSON WEB TOKEN,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可选的数字加密,可使用RSAECDSA进行公钥/私钥签名。

使用场景

JWT最常见的使用场景就是缓存当前用户登录信息,当用户登录成功之后,拿到JWT,之后用户的每一个请求在请求头携带上Authorization字段来辨别区分请求的用户信息。且不需要额外的资源开销。

相比传统session的区别

比起传统的session认证方案,为了让服务器能识别是哪一个用户发过来的请求,都需要在服务器上保存一份用户的登录信息(通常保存在内存中),再与浏览器的cookie打交道。

  • 安全方面 由于是使用cookie来识别用户信息的,如果cookie被拦截,用户会很容易受到跨站请求伪造的攻击。
  • 负载均衡 当服务器A保存了用户A的数据之后,在下一次用户A服务器A时由于服务器A访问量较大,被转发到服务器B,此时服务器B没有用户A的数据,会导致session失效。
  • 内存开销 随着时间推移,用户的增长,服务器需要保存的用户登录信息也就越来越多的,会导致服务器开销越来越大。

为什么说JWT不需要额外的开销

JWT为三个部分组成,分别是HeaderPayloadSignature,使用.符号分隔。

// 像这样子
xxxxx.yyyyy.zzzzz

标头 header

标头是一个JSON对象,由两个部分组成,分别是令牌是类型(JWT)和签名算法(SHA256RSA

{
  "alg": "HS256",
  "typ": "JWT"
}

负荷 payload

负荷部分也是一个JSON对象,用于存放需要传递的数据,例如用户的信息

{
  "username": "_island",
  "age": 18
}

此外,JWT规定了7个可选官方字段(建议)

属性 说明
iss JWT签发人
exp JWT过期时间
sub JWT面向用户
aud JWT接收方
nbf JWT生效时间
iat JWT签发时间
jti JWT编号

签章 signature

这一部分,是由前面两个部分的签名,防止数据被篡改。在服务器中指定一个密钥,使用标头中指定的签名算法,按照下面的公式生成这签名数据

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

在拿到签名数据之后,把这三个部分的数据拼接起来,每个部分中间使用.来分隔。这样子我们就生成出一个了JWT数据了,接下来返回给客户端储存起来。而且客户端在发起请求时,携带这个JWT在请求头中的Authorization字段,服务器通过解密的方式即可识别出对应的用户信息。

JWT优势和弊端

优势

  • 数据体积小,传输速度快
  • 无需额外资源开销来存放数据
  • 支持跨域验证使用

弊端

  • 生成出来的Token无法撤销,即使重置账号密码之前的Token也是可以使用的(需等待JWT过期)
  • 无法确认用户已经签发了多少个JWT
  • 不支持refreshToken

关于refreshToken

refreshTokenOauth2认证中的一个概念,和accessToken一起生成出来的。

当用户携带的这个accessToken过期时,用户就需要在重新获取新的accessToken,而refreshToken就用来重新获取新的accessToken的凭证。

为什么要有refreshToken

当你第一次接触的时候,你有没有一个这样子的疑惑,为什么需要refreshToken这个东西,而不是服务器端给一个期限较长甚至永久性的accessToken呢?

抱着这个疑惑我在网上搜寻了一番,

其实这个accessToken的使用期限有点像我们生活中的入住酒店,当我们在入住酒店时,会出示我们的身份证明来登记获取房卡,此时房卡相当于accessToken,可以访问对应的房间,当你的房卡过期之后就无法再开启房门了,此时就需要再到前台更新一下房卡,才能正常进入,这个过程也就相当于refreshToken

accessToken使用率相比refreshToken频繁很多,如果按上面所说如果accessToken给定一个较长的有效时间,就会出现不可控的权限泄露风险。

使用refreshToken可以提高安全性

  • 用户在访问网站时,accessToken被盗取了,此时攻击者就可以拿这个accessToke访问权限以内的功能了。如果accessToken设置一个短暂的有效期2小时,攻击者能使用被盗取的accessToken的时间最多也就2个小时,除非再通过refreshToken刷新accessToken才能正常访问。
  • 设置accessToken有效期是永久的,用户在更改密码之后,之前的accessToken也是有效的

总体来说有了refreshToken可以降低accessToken被盗的风险

关于JWT无感刷新TOKEN方案(结合axios)

业务需求

在用户登录应用后,服务器会返回一组数据,其中就包含了accessTokenrefreshToken,每个accessToken都有一个固定的有效期,如果携带一个过期的token向服务器请求时,服务器会返回401的状态码来告诉用户此token过期了,此时就需要用到登录时返回的refreshToken调用刷新Token的接口(Refresh)来更新下新的token再发送请求即可。

话不多说,先上代码

工具

axios作为最热门的http请求库之一,我们本篇文章就借助它的错误响应拦截器来实现token无感刷新功能。

具体实现

本次基于axios-bz[1]代码片段封装响应拦截器 可直接配置到你的项目中使用 ️ ️

利用interceptors.response,在业务代码获取到接口数据之前进行状态码401判断当前携带的accessToken是否失效。下面是关于interceptors.response中异常阶段处理内容。当响应码为401时,响应拦截器会走中第二个回调函数onRejected

下面代码分段可能会让大家阅读起来不是很顺畅,我直接把整份代码贴在下面,且每一段代码之间都添加了对应的注释

// 最大重发次数
const MAX_ERROR_COUNT = 5;
// 当前重发次数
let currentCount = 0;
// 缓存请求队列
const queue: ((t: string) => any)[] = [];
// 当前是否刷新状态
let isRefresh = false;

export default async (error: AxiosError<ResponseDataType>) => {
  const statusCode = error.response?.status;
  const clearAuth = () => {
    console.log('身份过期,请重新登录');
    window.location.replace('/login');
    // 清空数据
    sessionStorage.clear();
    return Promise.reject(error);
  };
  // 为了节省多余的代码,这里仅展示处理状态码为401的情况
  if (statusCode === 401) {
    // accessToken失效
    // 判断本地是否有缓存有refreshToken
    const refreshToken = sessionStorage.get('refresh') ?? null;
    if (!refreshToken) {
      clearAuth();
    }
    // 提取请求的配置
    const { config } = error;
    // 判断是否refresh失败且状态码401,再次进入错误拦截器
    if (config.url?.includes('refresh')) {
    clearAuth();
    }
    // 判断当前是否为刷新状态中(防止多个请求导致多次调refresh接口)
    if (!isRefresh) {
      // 设置当前状态为刷新中
      isRefresh = true;
      // 如果重发次数超过,直接退出登录
      if (currentCount > MAX_ERROR_COUNT) {
        clearAuth();
      }
      // 增加重试次数
      currentCount += 1;

      try {
        const {
          data: { access },
        } = await UserAuthApi.refreshToken(refreshToken);
        // 请求成功,缓存新的accessToken
        sessionStorage.set('token', access);
        // 重置重发次数
        currentCount = 0;
        // 遍历队列,重新发起请求
        queue.forEach((cb) => cb(access));
        // 返回请求数据
        return ApiInstance.request(error.config);
      } catch {
        // 刷新token失败,直接退出登录
        console.log('请重新登录');
        sessionStorage.clear();
        window.location.replace('/login');
        return Promise.reject(error);
      } finally {
        // 重置状态
        isRefresh = false;
      }
    } else {
      // 当前正在尝试刷新token,先返回一个promise阻塞请求并推进请求列表中
      return new Promise((resolve) => {
        // 缓存网络请求,等token刷新后直接执行
        queue.push((newToken: string) => {
          Reflect.set(config.headers!, 'authorization', newToken);
          // @ts-ignore
          resolve(ApiInstance.request<ResponseDataType<any>>(config));
        });
      });
    }
  }

  return Promise.reject(error);
};

抽离代码

把上面关于调用刷新token的代码抽离成一个refreshToken函数,单独处理这一情况,这样子做有利于提高代码的可读性和维护性,且让看上去代码不是很臃肿

// refreshToken.ts
export default async function refreshToken(error: AxiosError<ResponseDataType>) {
    /* 
    将上面 if (statusCode === 401) 中的代码贴进来即可,这里就不重复啦
    代码仓库地址: https://github.com/QC2168/axios-bz/blob/main/Interceptors/hooks/refreshToken.ts
    */
}

经过上面的逻辑抽离,现在看下拦截器中的代码就很简洁了,后续如果要调整相关逻辑直接在refreshToken.ts文件中调整即可。

import refreshToken from './refreshToken.ts'
export default async (error: AxiosError<ResponseDataType>) => {
  const statusCode = error.response?.status;

  // 为了节省多余的代码,这里仅展示处理状态码为401的情况
  if (statusCode === 401) {
    refreshToken()
  }

  return Promise.reject(error);
};


鸣谢:
https://mp.weixin.qq.com/s/Pt3ySxBWCmNswYsKIebPgQ

无感刷新 Token的更多相关文章

  1. Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Axios前后端分离模式下无感刷新实现JWT续期

    一. 前言 记得上一篇Spring Cloud的文章关于如何使JWT失效进行了理论结合代码实践的说明,想当然的以为那篇会是基于Spring Cloud统一认证架构系列的最终篇.但关于JWT另外还有一个 ...

  2. 基于OAuth2.0的token无感知刷新

    目前手头的vue项目关于权限一块有一个需求,其实架构师很早就要求我做了,但是由于这个紧急程度不是很高,最近临近项目上线,我才想起,于是赶紧补上这个功能.这个项目是基于OAuth2.0认证,需要在每个请 ...

  3. SpringSecurity+Oauth2+Jwt实现toekn认证和刷新token

    简单描述:最近在处理鉴权这一块的东西,需求就是用户登录需要获取token,然后携带token访问接口,token认证成功接口才能返回正确的数据,如果访问接口时候token过期,就采用刷新token刷新 ...

  4. ASP.NET Core 3.1使用JWT认证Token授权 以及刷新Token

    传统Session所暴露的问题 Session: 用户每次在计算机身份认证之后,在服务器内存中会存放一个session,在客户端会保存一个cookie,以便在下次用户请求时进行身份核验.但是这样就暴露 ...

  5. .Net中使用无闪刷新控件时提示框不显示

    今天做提示框的时候一直不显示,让我郁闷好久,晚上吃饭的时候问了同事一下,他给了一个思路, 他说可能是因为由于页面中的无闪刷新导致的结果:百度了一下真找到了解决方法 在页面中存在无闪刷新控件的时候提示框 ...

  6. WPF MVVM模式下的无阻塞刷新探讨

    很多时候我们需要做一个工作,在一个方法体里面,读取大数据绑定到UI界面,由于长时间的读取,读取独占了线程域,导致界面一直处于假死状态.例如,当应用程序开始读取Web资源时,读取的时效是由网络链路的速度 ...

  7. ArcEngine 图层无闪烁刷新

    使用AE的同行经常会遇到这样的问题,图层刷新.目前常用的有以下几种方法: 1.完全刷新 MapControl.Refresh(); 2.局部刷新 MapControl.Refresh(esriView ...

  8. flask刷新token

    我们在做前后端分离的项目中,最常用的都是使用token认证. 登录后将用户信息,过期时间以及私钥一起加密生成token,但是比较头疼的就是token过期刷新的问题,因为用户在登录后,如果在使用过程中, ...

  9. 十二、存token获取token刷新token发送header头

    //测试token //获取token function setToken(data){ var storage = window.localStorage; if(!storage){ alert( ...

  10. kaggle 欺诈信用卡预测——不平衡训练样本的处理方法 综合结论就是:随机森林+过采样(直接复制或者smote后,黑白比例1:3 or 1:1)效果比较好!记得在smote前一定要先做标准化!!!其实随机森林对特征是否标准化无感,但是svm和LR就非常非常关键了

    先看数据: 特征如下: Time Number of seconds elapsed between each transaction (over two days) numeric V1 No de ...

随机推荐

  1. drf-视图集、路由系统、action装饰器

    1.9个视图扩展类 1.两个视图基类:APIView.GenricAPIView 2.5个视图扩展类:CreateModelMixin,UpdateModelMixin,RetrieveModelMi ...

  2. 插入排序(CSP-J 2021 T2)

    题目:(由于题干过长直接上链接:P7910 [CSP-J 2021] 插入排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)) 不是打广告 又有一个新思路: 我们可以再开一个b数组 ...

  3. DownKyi安装使用教程

    哔哩下载姬 哔哩下载姬是一个简单易用的哔哩哔哩视频下载工具,具有简洁的界面,流畅的操作逻辑. 支持批量下载哔哩哔哩视频 支持批量下载bilibili视频. 哔哩下载姬可以下载几乎所有的B站视频,并输出 ...

  4. Apache Hudi 流转批 场景实践

    背景 在某些业务场景下,我们需要一个标志来衡量hudi数据写入的进度,比如:Flink 实时向 Hudi 表写入数据,然后使用这个 Hudi 表来支持批量计算并通过一个 flag 来评估它的分区数据是 ...

  5. JZOJ 4319. 【NOIP2015模拟11.5】Lucas的数列

    题目 思路 暴力很好打,我们显然可以先把关于 \(k\) 的式子拆开 先二项式展开,然后把外面的 \(m\) 乘进去,把 \(p\) 的分母 \(m\) 消去 \(K = (\sum_{i=1}^m ...

  6. Cobalt Strike 之:域内渗透

    郑重声明: 本笔记编写目的只用于安全知识提升,并与更多人共享安全知识,切勿使用笔记中的技术进行违法活动,利用笔记中的技术造成的后果与作者本人无关.倡导维护网络安全人人有责,共同维护网络文明和谐. Co ...

  7. 解析关于Tomcat Servlet-request的获取请求参数及几种常用方法

    摘要:本文主要讲解Tomcat之Servlet-request请求参数.Servlet转发机制.常用方法 本文分享自华为云社区<浅谈Tomcat之Servlet-request获取请求参数及常用 ...

  8. 基于Hexo的GitHub Pages个人博客搭建

    1.创建一个个人主页仓库 仓库命名最好为github账户名.github.io,这样可以通过https://github账户名.github.io访问 2.安装Hexo 此处采用局部安装: # npm ...

  9. PostgreSQL控制文件讲解及案例

    PostgreSQL控制文件内容: 主要分为是三部分,初始化静态信息.WAL及检查点的动态信息.一些配置信息. 我们可以用过pg_controldata命令直接读取PostgreSQL控制文件内容: ...

  10. Apache HttpClient使用和源码分析

    在上文中分析了 HttpURLConnection的用法,功能还是比较简单的,没有什么封装 接下来看看Apache HttpClient是如何封装httpClient的 目录 组成 请求代码 代码分析 ...