无感知刷新Token示例简析

针对“无感知刷新Token示例简析”,我将提供完整的攻略,分为以下几个部分:背景介绍、方案设计、示例说明及参考文献。

针对“无感知刷新Token示例简析”,我将提供完整的攻略,分为以下几个部分:背景介绍、方案设计、示例说明及参考文献。

背景介绍

随着Web应用不断扩大的规模和复杂度,用户态Token的安全性逐渐成为了不容忽视的问题,攻击者可以通过钓鱼、中间人等手段,窃取用户的Token,进而对用户的数据造成损失。为了解决这个问题,开发者可以通过刷新Token的方式,定期更换生成新的Token。但是在用户态下,对于token刷新的操作是不可避免的。
本文将介绍如何通过无感知的方式,刷新用户Token而不影响用户体验。

方案设计

为了实现无感知刷新Token的目的,我们可以采用以下的方案:

  1. 在每个需要用户验证的资源接口(比如API)中,都需要对Token进行校验,如果Token过期,则进行自动刷新。

  2. 在Token过期时,需要发出异步请求,在后端重新生成Token,并将新Token存储在数据库或Redis等存储介质中,并将新Token返回给前端。

  3. 在前端接收到新Token的响应后,我们需要将新Token更新到本地存储中。此时无需重载页面即可进行无感刷新Token,之后用户的操作需要使用新Token(否则操作会一直被forbidden)。

示例说明

以JavaScript的Fetch请求为例,以下提供两种实现无感知刷新Token的示例:

  1. 使用Fetch Interceptor
import auth from './auth'

// 构建fetch拦截器
class FetchInterceptor {
  static instance

  // 获取拦截器实例
  static getInstance () {
    if (!this.instance) {
      this.instance = new FetchInterceptor()
    }
    return this.instance
  }

  // 请求拦截器,用于自动注入请求头
  interceptRequest (url, config) {
    // 从本地存储中获取token
    const token = auth.getToken()

    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }

    return [url, config]
  }

  // 响应拦截器,自动刷新过期token
  async interceptResponse (response) {
    // 访问受限,判断是否token过期
    const data = await response.json()
    if (response.status === 401) {
      // 过期了,发起刷新token的请求,将新的token存储到本地存储中
      const newToken = await auth.refreshToken()
      // 存储新的token
      auth.saveToken(newToken)

      // 构建新的请求headers
      const headers = new Headers(response.headers)
      headers.set('Authorization', `Bearer ${newToken}`)

      // 构建新的响应对象
      let newResponse = new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers
      })
      return newResponse
    }

    // 非401请求直接返回
    return response
  }
}

// 处理完 intercept 链之后的 request 函数
async function intercept (url, config) {
  const interceptors = [FetchInterceptor.getInstance()]
  let request = [url, config]

  // 按照顺序来执行拦截器
  for (let i = 0; i < interceptors.length; i++) {
    const [newUrl, newConfig] = await interceptors[i].interceptRequest(...request)
    request = [newUrl, newConfig]
  }

  const response = await fetch(...request)

  // 针对返回值做hack处理
  for (let i = interceptors.length - 1; i >= 0; i--) {
    const newResponse = await interceptors[i].interceptResponse(response)
    response = newResponse || response
  }

  return response
}

function get (url) {
  return intercept(url, { method: 'GET' })
}

export default {
  get
}
  1. 使用Axios Interceptor
import axios from 'axios'
import auth from './auth'

// 初始化Axios实例
const instance = axios.create()

// 拦截器配置
instance.interceptors.request.use(
  (config) => {
    // 从本地存储中获取token
    const token = auth.getToken()

    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }

    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

instance.interceptors.response.use(
  (response) => {
    return response
  },
  async (error) => {
    const {
      config,
      response: { status }
    } = error

    if (status === 401) {
      // 获取新的token
      const newToken = await auth.refreshToken()

      // 更新本地token
      auth.saveToken(newToken)

      // 重新加载原有请求
      config.headers.Authorization = `Bearer ${newToken}`
      return instance.request(config)
    }

    return Promise.reject(error)
  }
)

export default instance

参考文献

文章内容参考了以下资料:

  1. Refresh JWT token automatically with Axios interceptor

  2. Vue知识库-无感知刷新Token示例简析

本文标题为:无感知刷新Token示例简析

基础教程推荐