一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
大家好,我是大帅子,今天给大家讲一下路由前置守卫,下面我们直接开始吧
401问题-背景介绍
目标
了解401错误的现象、原因、refresh_token的作用
401错误的场景
401是http的状态码,表示本次请求没有权限;
有如下两种情况会出现401错误:
- 未登陆用户做一些需要权限才能做的操作(例如:关注作者),network中会提示本次请求是401错误
- 登录用户的token过期了
理解token过期
登陆成功之后,接口会返回一个token值,这个值在后续请求时通过请求头时带上(就像是进入小区门的开门钥匙)。但是,这个值一般会有有效期(具体是多长,是由后端决定),在我们的项目中,这个有效期是2小时。
如果你上午8点登陆成功,到了10:01分,则token就会失效,再去发请求时,就会报401错误。
双token
refresh_token和token的作用
当用户登陆成功之后,返回的token中有两个值,说明如下:
-
token:
- 作用:一般情况下,在访问接口时,需要传入的token值就是它。
- 有效期:2小时(安全)。
-
refresh_token
- 作用: 当token的有效期过了之后,可以使用refresh_token去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
- 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)
401问题-思路介绍
目标
学习响应拦截器的使用,分析处理401问题的思路。
响应拦截器功能
axios中提供了响应拦截器功能:所有从后端回来的响应都会先进入响应拦截器,包括出错的请求中。所以,我们可以在响应拦截器中去写代码来统一解决。
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
return response
}, async function (error) {
// 如果发生了错误,判断是否是401
console.dir(error)
// 开始处理
return Promise.reject(error)
})
401问题的思路
让用户对因token过期产生的401错误无感知
request的响应拦截器中:
-
对于某次请求A,如果是401错误 (2)
-
有refresh_token,用refresh_token去请求回新的token (3)
-
新token请求成功 (4)
- 更新本地token (5)
- 再发一次请求A (6)
-
新token请求失败
- 携带请求地址,跳转到登陆页
-
-
没有refresh_token,说明没有登陆
- 携带请求地址,跳转到登陆页
-
401问题-代码
在src\utils\request.ts中,补充响应拦截器。
由于这里涉及到非组件内路由跳转,所以需要提前封装独立的history
封装独立的history
创建文件 src\utils\history.ts
import { createBrowserHistory } from 'history'
const history = createBrowserHistory()
export default history
在app.tsx中使用
import history from '@/utils/history'
function App () {
return (
<div className="app">
<Router history={history}>
补充action
在actions/login.ts中,补充一个action用来去做更新token的操作
export function saveToken (token: Token) : LoginAction {
// 1. 本地持久化
setToken(token)
// 2. 保存token
return {
type: 'login/token',
payload: token
}
}
核心代码
要进行路由跳转,所以要引入/history.js中的history。
在request.ts中
,添加响应拦截器
// 添加响应拦截器
instance.interceptors.response.use(
function (response) {
// 对响应数据做点什么
return response
},
async function (error: AxiosError<{message: string}>) {
// 对请求错误做些什么
if (!error.response) {
Toast.show('网络异常')
return Promise.reject(error)
}
if (error.response.status !== 401) {
Toast.show('后端错误')
return Promise.reject(error)
}
const { refresh_token } = getToken()
if (!refresh_token) {
// alert('refresh_token')
history.replace('/login', { from: history.location.pathname })
Toast.show('登录信息过期')
return Promise.reject(error)
}
try {
// 使用refresh_token去换取新token
// 使用 axios 来发送请求
const res = await axios.put(`${baseURL}authorizations`, null, {
headers: {
Authorization: `Bearer ${refresh_token}`
}
})
// setToken(tokens)
store.dispatch(saveToken({ token: res.data.data, refresh_token }))
return instance(error.config)
} catch (e) {
// setToken(tokens)
store.dispatch(logout())
history.replace('/login', { from: history.location.pathname })
return Promise.reject(e)
}
}
)
注意:
响应拦截器要加在axios实例 request上。
用refresh_token请求新token时,要用axios,不要用实例request
得到新token之后,再发请求时,要用request实例
好了,这边已经给大家介绍到这里,以上是我自己的理解,希望可以帮到大家, 欢迎留言我这边一定会第一时间给大家解答,喜欢的可以点赞收藏,
🐣---->🦅 还需努力!大家一起进步!!!