无感刷新的基本原理
- 使用刷新令牌(refresh token):
○ 应用程序在首次登录成功后会获得一个访问令牌(access token)和一个刷新令牌(refresh token)。
○ 访问令牌通常有较短的有效期,而刷新令牌的有效期较长。 - 自动刷新:
○ 当访问令牌即将过期时,应用程序会在后台使用刷新令牌来获取新的访问令牌。
○ 这个过程通常是透明的,用户不会察觉到任何变化。
业务场景及双token的处理流程图
- 请求登录接口拿到两个token,在响应拦截器中存储在本地
service.interceptors.response.use(async function (response) {// 对响应数据做点什么if (response.data.code === 0) {response.data.data.token &&localStorage.setItem("token", response.data.data.token);response.data.data.refresh_token &&localStorage.setItem("refresh_token", response.data.data.refresh_token);return response.data.data;} return response;},function (error) {// 对响应错误做点什么return Promise.reject(error);}
);
- 提交业务表单时候,请求拦截器中请求头携带上短token
service.interceptors.request.use(function (config) {// 在发送请求之前做些什么// tokenif (config.url !== "/login") {config.headers["Authorization"] = `Bearer ${localStorage.getItem("token")}`;}return config;},function (error) {// 对请求错误做些什么return Promise.reject(error);}
);
const onSubmit = ()=>{postFrom().then(res=>{if(res.content){ElMessage.success('提交成功')dialogVisible.value = false}else{//token失效,跳转登录页router.push('/login')}})
}
- 如果此时短token失效
- 通过请求getRefresh()接口获取新的token
在发起请求之前,需要在请求拦截器中设置携带长token
service.interceptors.request.use(function (config) {if (config.url === "/refresh_token") {config.headers["Authorization"] = `Bearer ${localStorage.getItem("refresh_token")}`;}return config;},
);
service.interceptors.response.use(async function (response) {// 对响应数据做点什么if (response.data.code === 0) {......} else if (response.data.code === 401) {console.log('token过期');// 请求刷新tokenconst result = await getRefresh();console.log(result);}return response;},
);
- 获取到新的token之后,设置请求头携带新token,再次发起提交表单
if (result.token) {console.log('getRefresh()携带长token去重新获取短token', result);//拿到新的token之后,设置或更新请求头中的 Authorization 字段。response.config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`;// 再次发起提交表单的接口/protectedreturn service.request(response.config);}
此时已经实现,无感刷新token
- 假设长token也失效了,则跳转登录页重新获取 长短新token
let isRefreshing = false; //标记是否正在刷新token
// 添加响应拦截器
service.interceptors.response.use(async function (response) {// 对响应数据做点什么if (response.data.code === 0) {response.data.data.token &&localStorage.setItem("token", response.data.data.token);response.data.data.refresh_token &&localStorage.setItem("refresh_token", response.data.data.refresh_token);return response.data.data;} else if (response.data.code === 401) {if (!response.config._retry && !isRefreshing ) {console.log('token过期');isRefreshing = true; // 标记为正在刷新tokenresponse.config._retry = true;// 请求刷新tokenconst result = await getRefresh();console.log(result);//之所以不需要再次设置新的短token,是因为getRefresh()接口的状态码是0,所以在上面已经设置过新tokenif (result.token) {console.log('getRefresh()携带长token去重新获取短token', result);//拿到新的token之后,设置或更新请求头中的 Authorization 字段。response.config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`;isRefreshing = false;// 再次发起提交表单的接口/protectedreturn service.request(response.config);}} else if (isRefreshing) { // 已经在刷新,不再重复刷新console.log('长token也过期了,跳转登录页');isRefreshing = false;ElMessage.error('请重新登录');}}return response;},function (error) {// 对响应错误做点什么return Promise.reject(error);}
);
完整代码
青稞儿/双token无感刷新