如何处理Access Token过期并使用Refresh Token获取新的Access Token
解决方案:
-
在登录成功时,服务器返回给客户端两个Token:Access Token和Refresh Token。Access Token用于访问受保护的资源,而Refresh Token用于获取新的Access Token。
-
将Refresh Token存储在客户端(例如,浏览器的Cookie或本地存储)中,以便在Access Token过期后使用。
-
在每次请求受保护的资源时,客户端将Access Token附加到请求的Header中。服务器将验证Access Token的有效性,如果过期则返回401 Unauthorized错误。
-
当服务器返回401 Unauthorized错误时,客户端需要捕获该错误并发送一个刷新令牌的请求。
-
在客户端发送刷新令牌的请求时,将Refresh Token附加到请求中,并将其发送到服务器。
-
服务器验证Refresh Token的有效性,并生成一个新的Access Token。如果Refresh Token无效,则返回401 Unauthorized错误。
-
客户端接收到新的Access Token后,更新存储的Access Token,并重新发送之前失败的请求,并将新的Access Token附加到请求中。
-
客户端继续访问受保护的资源,并在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';
// 获取列表
export function fetchList(params) {
return request({
url: '/list',
method: 'get',
params
});
}
// 创建列表
export function createList(data) {
return request({
url: '/list',
method: 'post',
data
});
}
// 更新列表
export function updateList(data) {
return request({
url: '/list',
method: 'put',
data
});
}
// 删除列表
export function removeList(data) {
return request({
url: '/list',
method: 'delete',
data
});
}
// 刷新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。