实现无感刷新token(看这一篇就够了)

一、什么是无感刷新token?

无感知刷新Token是指,在Token过期之前,系统自动使用Refresh Token获取新的Access Token,从而实现Token的无感知刷新,用户可以无缝继续使用应用。

在实现无感知刷新Token的过程中,需要考虑以下几个方面:

  • 如何判断Token是否过期?
  • 如何在Token过期时自动使用Refresh Token获取新的Access Token?

下面将介绍如何实现无感知刷新Token的具体步骤。

实现步骤

步骤一:获取AccessToken和RefreshToken。

在认证成功之后,我们需要将后端生成的AccessToken和RefreshToken发送给客户端。并将返回的token保存到本地缓存。AccessToken用于访问受保护的API,RefreshToken用于获取新的AccessToken。我们可以使用JWT(jsonwebtoken)来实现认证。

我们在后端设置生成token以及验证token是否过期的方法。

const jwt = require('jsonwebtoken')

let encryption= 'encryption'
const createToken = {
    // 生成token
    getToken(jiamiData,expiresIn){
        return jwt.sign({
            data:jiamiData,
        },encryption,{expiresIn:expiresIn})
    },
    verify(token){
        try{
            return jwt.verify(token,encryption)
        }catch(error){
            return false
        }
    }
}
module.exports = createToken

然后在我们进行认证的时候调用生成token的方法getToken()。

  let accessToken = createToken.getToken('user','5s');
  let refreshToken = createToken.getToken('user','10s');

步骤二:设置请求拦截器,在请求中携带AccessToken。

在这里呢,我们就需要对axios进行二次封装,然后设置请求拦截器,在请求拦截器中给每个需要认证的API的请求头中携带AccessToken。AccessToken可以在我们本地缓存中拿到。如下所示:

// 进行axios二次封装:使用请求和响应拦截器
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 第一步:利用axios对象的create方法,去创建axios实例(其他配置:基础路径、超时的时间)
const request = axios.create({
  // 基础路径
  baseURL: 'http://localhost:3000', //基础路径上会携带/api
  timeout: 5000, //超时时间的设置
})
// 第二步:request实例添加请求与响应拦截器
request.interceptors.request.use(
  (config) => {
    // cnofig配置对象,headers属性请求头,经常给服务器携带公共参数
    //返回配置对象
    // 在发送请求之前做些什么
    if (localStorage.getItem('AccessToken')) {
      config.headers.Authorization =
        'Bearer ' +
          JSON.parse(JSON.stringify(localStorage.getItem('AccessToken'))) || ''
    }
    // 返回配置对象
    return config
  },
  (error) => {
    // 对请求错误做些什么
    return Promise.reject(error)
  },
)

 步骤三:通过AccessToken向后端请求受保护数据。

在向后端请求数据的时候,后端会先获取到AccessToken进行判断是否过期,如果没有过期就会正常返回用户数据,如果过期了就会返回401状态码,并提示AccessToken已经过期了,无法返回用户数据。如果我们本地缓存中不存在AccessToken就会返回406状态码,并提示无效的刷新令牌。

router.get("/user", async (req, res) => {
  const username = req.query.username;
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(" ")[1]; // 提取Bearer JWT token 
    // 在这里可以对token进行验证和解码等操作
    // 然后在后续的处理中使用解码后的信息
    if (createTokenCheck.verify(token)) {
      let data = await usersModel.find({ username });
      res.send({
        code: 200,
        data,
      });
    } else {
      res.send({
        code: 401,
        data: "token已过期",
      });
    }
  } else {
    res.send({
      code: 406,
      msg: "无效的刷新令牌",
    });
  }
});

 步骤四:设置相应拦截器,拦截后端返回的信息并判断是否需要刷新RefreshToken。

在相应拦截器中,会拦截到服务器返回的数据,首先会判断是否是有效的AccessToken,如果不是就需要用户去重新进行认证,如果是有效的AccessToken就回去判断它是否过期,如果没有过期就会把数据返回给用户,如果过期了就会去调用refresh接口,将本地缓存中的RefreshToken作为参数传递给后端,在后端会去判断RefreshToken是否过期如果过期就需要用户重新认证,如果没有过期就会给客户端重新返回一组新生成的AccessToken和RefreshToken,并重新保存到本地缓存,将已经过期的AccessToken刷新,再次向后端发送一次请求,获取用户数据。

// 第三步:响应拦截器
request.interceptors.response.use(
  async (response) => {
    // 结构一层数据
    const dataAxios = response
    if (
      dataAxios.data.msg == '刷新令牌已过期' ||
      dataAxios.data.msg == '无效的刷新令牌'
    ) {
      ElMessage.error('登录凭证过期,请重新登陆') //错误信息
      localStorage.clear() // 删除缓存
      isRefreshing = false // 请求成功,开启刷新标识
      requests = [] // 置空
      router.push('/login')
      return false
    }
    // 无感刷新token
    if (dataAxios.data.code === 401) {
      if (!isRefreshing) {
        isRefreshing = true
        // 获取本地缓存中的RefreshToken
        const RefreshToken = localStorage.getItem('RefreshToken')
        // 请求新的token
        const res = await refreshToken(RefreshToken)
        // 请求成功,开启刷新标识
        isRefreshing = false
        localStorage.setItem('AccessToken', res.accessToken)
        localStorage.setItem('RefreshToken', res.refreshToken)
        // 已经刷新了token,将所有队列中的请求进行重试
        requests.forEach((item) => item(res?.accessToken))
        requests = []
      }
      return new Promise((resolve) => {
        // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
        requests.push((token) => {
          response.config.headers.Authorization = `Bearer ${token}`
          resolve(request(response.config)) //执行请求,
        })
      })
    }
    // 错误信息
    if (!dataAxios.data && dataAxios.data.code != 0) {
      ElMessage.error(dataAxios.data.msg)
    }
    return dataAxios.data
  }
)

步骤五:刷新RefreshToken的接口设置。

router.get("/refresh", async (req, res) => {
  const token = req.query.token;
  let accessToken = createTokenCheck.getToken("user", "10s");
  let refreshToken = createTokenCheck.getToken("user", "60s");
  if (createTokenCheck.verify(token)) {
    res.send({
      code: 200,
      refreshToken,
      accessToken,
    });
  } else {
    console.log("refresh");
    res.send({
      code: 401,
      msg: "刷新令牌已过期",
    });
  }
});

二、无感刷新token的优点

用户体验改善:无感刷新Token技术可以在用户无感知的情况下,自动刷新访问令牌,避免了用户在过期后需要重新登录的操作,提高了用户体验。
安全性提升:通过无感刷新Token技术,可以减少因为Token过期而导致的用户访问权限问题。同时,在访问令牌过期后,通过自动刷新获取新的访问令牌,避免了客户端传输和保存用户凭证(如用户名和密码)的风险。
降低服务端压力:相比于每次请求都需要服务端检查访问令牌的有效性,无感刷新Token技术将刷新操作从服务端转移到了客户端,减轻了服务端的负担,提高了服务端的并发处理能力。

三、应用场景

Web应用或移动应用中:在Web应用或移动应用中,通过无感刷新Token技术,可以实现用户登录后,自动刷新访问令牌,使用户无需在过期后重新登录。
单页应用(SPA)中:在单页应用中,页面无刷新,因此无法通过传统方式进行Token刷新。通过无感刷新Token技术,可以在后台自动刷新访问令牌,保持用户会话的持续性。
使用长时间访问令牌的应用:有些应用使用长时间访问令牌(比如30天),为了保证安全性,需要定期刷新访问令牌。无感刷新Token技术可以方便地实现这个刷新过程。
多终端登录应用:在多终端登录的应用中,如电子商务平台或社交媒体,用户可能会在不同的设备上同时登录。通过无感刷新Token技术,可以保持用户在各个设备上的登录状态同步。

四、总结

   无感刷新Token技术是一种在客户端应用中自动刷新访问令牌的机制,它通过提供用户无感知的刷新操作,改善了用户体验,提高了安全性,同时减轻了服务端的负担。这项技术适用于各种Web应用、移动应用和单页应用等场景,特别适用于需要长时间访问令牌和多终端登录的应用。通过无感刷新Token技术,我们能够在保证用户会话持续性的同时,提供更好的安全性和便利性。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值