实现Token过期处理和刷新机制

如何处理Access Token过期并使用Refresh Token获取新的Access Token

解决方案:

  1. 在登录成功时,服务器返回给客户端两个Token:Access Token和Refresh Token。Access Token用于访问受保护的资源,而Refresh Token用于获取新的Access Token。

  2. 将Refresh Token存储在客户端(例如,浏览器的Cookie或本地存储)中,以便在Access Token过期后使用。

  3. 在每次请求受保护的资源时,客户端将Access Token附加到请求的Header中。服务器将验证Access Token的有效性,如果过期则返回401 Unauthorized错误。

  4. 当服务器返回401 Unauthorized错误时,客户端需要捕获该错误并发送一个刷新令牌的请求。

  5. 在客户端发送刷新令牌的请求时,将Refresh Token附加到请求中,并将其发送到服务器。

  6. 服务器验证Refresh Token的有效性,并生成一个新的Access Token。如果Refresh Token无效,则返回401 Unauthorized错误。

  7. 客户端接收到新的Access Token后,更新存储的Access Token,并重新发送之前失败的请求,并将新的Access Token附加到请求中。

  8. 客户端继续访问受保护的资源,并在Access Token过期前重复上述步骤。

1. request.js

首先,我们在request.js中添加拦截器来处理Token过期和刷新。

import axios from 'axios';
import { MessageBox, Message } from 'element-ui';
import store from '@/store';
import { getToken, setToken, removeToken } from '@/utils/auth';
import { refreshToken as refreshAuthToken } from '@/utils/api'; // 引入刷新token的API方法

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // api的base_url
  timeout: 5000 // 请求超时时间
});

// request拦截器
service.interceptors.request.use(
  config => {
    // 在请求发送之前做一些事情
    if (store.getters.token) {
      // 让每个请求携带token-- ['Authorization']为自定义key 请根据实际情况自行修改
      config.headers['Authorization'] = `Bearer ${getToken()}`;
    }
    return config;
  },
  error => {
    // Do something with request error
    console.log(error); // for debug
    return Promise.reject(error);
  }
);

// response拦截器
service.interceptors.response.use(
  response => {
    /**
     * code为非20000是抛错 可结合自己业务进行修改
     */
    const res = response.data;
    if (res.code !== 20000) {
      Message({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      });

      // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // 请自行在引入 MessageBox
        MessageBox.confirm(
          '你已被登出,可以取消继续留在该页面,或者重新登录',
          '确定登出',
          {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning'
          }
        ).then(() => {
          store.dispatch('FedLogOut').then(() => {
            location.reload(); // 为了重新实例化vue-router对象 避免bug
          });
        });
      }
      return Promise.reject('error');
    } else {
      return response.data;
    }
  },
  async error => {
    console.log('err' + error); // for debug
    const originalRequest = error.config;

    if (error.response && error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true; // 避免了可能出现的无限循环重试请求的情况
      try {
        const tokenResponse = await refreshAuthToken();
        const newToken = tokenResponse.data.accessToken;
        setToken(newToken);
        originalRequest.headers['Authorization'] = 'Bearer ' + newToken;
        return service(originalRequest);
      } catch (refreshError) {
        store.dispatch('FedLogOut').then(() => {
          location.reload();
        });
      }
    }

    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    });
    return Promise.reject(error);
  }
);

export default service;

2. api.js

在api.js中添加刷新Token的方法。

import request from '@/utils/request';

// 刷新Token
export function refreshToken() {
  return request({
    url: '/auth/refresh-token',
    method: 'post',
    data: {
      refreshToken: getToken('refreshToken') // 获取存储的Refresh Token
    }
  });
}

3. auth.js

在utils目录下创建一个auth.js文件,用于管理Token的存储和获取。

import Cookies from 'js-cookie';

const TokenKey = 'Admin-Token';
const RefreshTokenKey = 'Admin-Refresh-Token';

// 获取Access Token
export function getToken() {
  return Cookies.get(TokenKey);
}

// 设置Access Token
export function setToken(token) {
  return Cookies.set(TokenKey, token);
}

// 移除Access Token
export function removeToken() {
  return Cookies.remove(TokenKey);
}

// 获取Refresh Token
export function getRefreshToken() {
  return Cookies.get(RefreshTokenKey);
}

// 设置Refresh Token
export function setRefreshToken(token) {
  return Cookies.set(RefreshTokenKey, token);
}

// 移除Refresh Token
export function removeRefreshToken() {
  return Cookies.remove(RefreshTokenKey);
}

4. store.js

在store.js中添加登出操作。

import { removeToken, removeRefreshToken } from '@/utils/auth';

const actions = {
  FedLogOut({ commit }) {
    return new Promise(resolve => {
      commit('SET_TOKEN', '');
      removeToken();
      removeRefreshToken();
      resolve();
    });
  }
};

export default {
  namespaced: true,
  actions
};

5. 登录逻辑

在登录成功后,需要将Access Token和Refresh Token存储起来。

import { setToken, setRefreshToken } from '@/utils/auth';

login().then(response => {
  const { accessToken, refreshToken } = response.data;
  setToken(accessToken);
  setRefreshToken(refreshToken);
});

通过以上步骤,你就可以实现Token过期自动刷新、重新请求的功能。确保在服务器端实现相应的刷新Token接口,并返回新的Access Token和Refresh Token。

posted @ 2024-06-13 17:35  苏沐~  阅读(289)  评论(0编辑  收藏  举报