Axios 全局配置401刷新token并且再次请求
VoidWh 2021-04-14
Axios
token
踩过的坑
# 背景
OAuth2, 启用access_token(有效期10分钟), refresh_token(有效期20分钟)
# 需要解决的问题
- 用户10分钟有效活动时间
- 第11分钟到20分钟之间,用户只要发送一次请求,就进行token刷新操作,用 refresh_token 作为header 里的 token 调用刷新 token 的 api,成功则进行token更新的操作,失败则进行登出操作
- 刷新token期间所有请求在刷新 token 后,重新进行请求
# 实现
import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/core/store'
import {
getToken,
getRefreshToken,
setRefreshToken,
setToken
} from '@/core/utils/auth'
import { refreshToken } from '@/core/basic/api'
// create an axios instance
const request = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 15000 // request timeout
})
// request interceptor
request.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// 如果 token 存在
// 让每个请求携带自定义 token 请根据实际情况自行修改
config.headers.common.Authorization = `bearer ${getToken()}`
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// refresh status
let isRefreshing = false
let retryRequests = []
// response interceptor
request.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
return res
},
error => {
console.error(error.response.data)
if ([560].includes(error.response.status) && error.response.data) {
// 別のところに既にログインしましたので、再ログインが必要です。再ログインしてください!
if (error.response.data.code === 400) {
setTimeout(() => {
window.location = '/login'
}, 5 * 1000)
}
Message({
message: error.response.data.msg,
type: 'error',
duration: 5 * 1000
})
} else if ([403].includes(error.response.status) && error.response.data) {
Message({
message: error.response.data.detail.msg,
type: 'error',
duration: 5 * 1000
})
store.dispatch('user/logout')
window.location = '/login'
} else if ([401].includes(error.response.status)) {
// need refresh token
const config = error.config
if (getRefreshToken()) {
if (!isRefreshing) {
isRefreshing = true
return refreshToken(getRefreshToken())
.then(res => {
if (res.data.access_token && res.data.refresh_access_token) {
setToken(res.data.access_token)
setRefreshToken(res.data.refresh_access_token)
config.headers['Authorization'] = `bearer ${getToken()}`
retryRequests.forEach(item => {
item()
})
retryRequests = []
return request(config)
}
})
.catch(() => {
// refresh failed
// logout
store.dispatch('user/logout')
window.location = '/login'
Message({
message: '再ログインしてください。',
type: 'error',
duration: 5 * 1000
})
})
.finally(() => {
isRefreshing = false
})
} else {
return new Promise(resolve => {
retryRequests.push(() => {
config.headers['Authorization'] = `bearer ${getToken()}`
resolve(request(config))
})
})
}
} else {
store.dispatch('user/logout')
window.location = '/login'
Message({
message: '再ログインしてください。',
type: 'error',
duration: 5 * 1000
})
}
} else if ([500].includes(error.response.status)) {
Message({
message: 'サーバーエラー',
type: 'error',
duration: 5 * 1000
})
} else {
Message({
message: error.response.data.detail,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(error)
}
)
// noAuthRequest
const noAuth = require('axios')
const noAuthRequest = noAuth.create()
// response interceptor
noAuthRequest.interceptors.response.use(
response => {
return response
},
error => {
console.error(error.response.data)
if ([560].includes(error.response.status) && error.response.data) {
Message({
message: error.response.data.msg,
type: 'error',
duration: 5 * 1000
})
} else if ([403].includes(error.response.status) && error.response.data) {
Message({
message: error.response.data.detail.msg,
type: 'error',
duration: 5 * 1000
})
window.location = '/login'
} else {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(error)
}
)
export { noAuthRequest, request }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181