Token无感刷新
前端Token无感刷新机制详解与实现
在现代Web应用中,使用Token进行身份验证是一种常见且高效的方法。然而,Token的有效期有限,当其过期时,用户需要重新登录才能继续访问受保护的资源,这会打断用户体验。为了解决这一问题,引入了Token无感刷新机制,即在Token即将过期前自动刷新,使用户在无感知的情况下继续操作。本文将详细介绍Token无感刷新的工作原理,并提供基于Vue.js和Axios的完整代码示例。
Token无感刷新原理
1. 双Token机制
通常采用两种类型的Token:
- Access Token:用于直接访问受保护资源。
- Refresh Token:用于获取新的Access Token。
2. Token生命周期
- 用户登录时,服务器返回Access Token和Refresh Token。
- Access Token具有较短的有效期,一般几分钟到几小时。
- Refresh Token有效期较长,可以是一天或更长。
3. 自动刷新流程
- 客户端尝试访问资源:使用当前的Access Token。
- 检测Token过期:如果Access Token过期,客户端使用Refresh Token请求新的Access Token。
- 服务器验证并返回新Token:服务器验证Refresh Token有效后,返回新的Access Token。
- 客户端存储新Token:客户端存储新的Access Token,并用它继续访问资源。
Vue.js + Axios代码示例
环境准备
确保你的开发环境中安装了Node.js和npm/yarn,并创建了一个Vue.js项目。
步骤1: 安装依赖
在项目根目录下运行以下命令来安装Axios和Vuex:
npm install axios vuex
步骤2: 配置Axios实例
创建一个axios.js文件,在其中设置全局Axios实例,并添加拦截器用于Token刷新。
// src/axios.js
import axios from 'axios';
import store from '@/store'; // 引入Vuex store
const instance = axios.create({
baseURL: 'http://your-api-url.com',
});
let isRefreshing = false; // 防止同时刷新Token
let failedQueue = []; // 存储失败的请求
// 请求拦截器
instance.interceptors.request.use(config => {
const token = store.getters['auth/token'];
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器
instance.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
if (!isRefreshing) {
isRefreshing = true;
return store.dispatch('auth/refreshToken')
.then(newAccessToken => {
isRefreshing = false;
axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
failedQueue.forEach(request => request.onSuccess());
failedQueue = [];
})
.catch(err => {
isRefreshing = false;
failedQueue.forEach(request => request.onFailure());
failedQueue = [];
if (error.config && error.config.reject) {
error.config.reject(err);
}
});
}
return new Promise((resolve, reject) => {
failedQueue.push({
onSuccess: () => {
originalRequest.headers.Authorization = `Bearer ${store.getters['auth/token']}`;
resolve(axios(originalRequest));
},
onFailure: () => {
reject(error);
},
});
});
}
return Promise.reject(error);
}
);
export default instance;
步骤3: 配置Vuex Store
在store目录下创建index.js,用来管理Token和刷新Token的动作。
// src/store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {
accessToken: localStorage.getItem('accessToken') || '',
refreshToken: localStorage.getItem('refreshToken') || '',
},
getters: {
'auth/token': state => state.accessToken,
},
mutations: {
'auth/setTokens'(state, { accessToken, refreshToken }) {
state.accessToken = accessToken;
state.refreshToken = refreshToken;
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('refreshToken', refreshToken);
},
},
actions: {
'auth/login'({ commit }, credentials) {
return axios.post('/api/auth/login', credentials)
.then(response => {
const { accessToken, refreshToken } = response.data;
commit('auth/setTokens', { accessToken, refreshToken });
});
},
'auth/refreshToken'({ state, commit }) {
return axios.post('/api/auth/refresh', { refreshToken: state.refreshToken })
.then(response => {
const { accessToken } = response.data;
commit('auth/setTokens', { accessToken, refreshToken: state.refreshToken });
return accessToken;
});
},
},
});
步骤4: 登录和刷新Token
在登录组件中,使用auth/login action进行登录,获取初始的Access Token和Refresh Token。
<!-- views/Login.vue -->
<template>
<div>
<form @submit.prevent="login">
<!-- 表单字段 -->
<button type="submit">登录</button>
</form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['auth/login']),
login() {
this.auth.login({
username: 'your_username',
password: 'your_password',
});
},
},
};
</script>

浙公网安备 33010602011771号