oauth-2.0 - 通过 axios 中的拦截器自动刷新访问令牌
问题描述
我们最近在这个问题中讨论了用于 OAuth 身份验证令牌刷新的 axios 拦截器。
拦截器应该做的是拦截任何带有401
状态码的响应并尝试刷新令牌。考虑到这一点,接下来要做的是从拦截器返回一个 Promise,这样任何通常会失败的请求都会在令牌刷新后运行,因为没有任何反应。
主要问题是,拦截器只检查401
状态码,这还不够,因为它refreshToken
也会401
在失败时返回状态码——而且我们有一个循环。
我想到了两种可能的情况:
- 检查调用的 URL,所以如果是这样,
/auth/refresh
它不应该尝试刷新令牌; refreshToken
调用逻辑时省略拦截器
第一个选项对我来说看起来有点“不动态”。第二种选择看起来很有希望,但我不确定它是否可能。
那么主要的问题是,我们如何区分/识别拦截器中的调用并为它们运行不同的逻辑而无需专门“硬编码”它,或者有什么方法可以省略指定调用的拦截器?先感谢您。
拦截器的代码可能有助于理解这个问题:
Axios.interceptors.response.use(response => response, error => {
const status = error.response ? error.response.status : null
if (status === 401) {
// will loop if refreshToken returns 401
return refreshToken(store).then(_ => {
error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
error.config.baseURL = undefined;
return Axios.request(error.config);
})
// Would be nice to catch an error here, which would work if the interceptor is omitted
.catch(err => err);
}
return Promise.reject(error);
});
和令牌刷新部分:
function refreshToken(store) {
if (store.state.auth.isRefreshing) {
return store.state.auth.refreshingCall;
}
store.commit('auth/setRefreshingState', true);
const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
store.commit('auth/setToken', token)
store.commit('auth/setRefreshingState', false);
store.commit('auth/setRefreshingCall', undefined);
return Promise.resolve(true);
});
store.commit('auth/setRefreshingCall', refreshingCall);
return refreshingCall;
}
解决方案
我可能找到了一种更简单的方法来处理这个问题:当我调用 /api/refresh_token 端点时,使用 axios.interceptors.response.eject() 禁用拦截器,然后重新启用它。
编码 :
createAxiosResponseInterceptor() {
const interceptor = axios.interceptors.response.use(
response => response,
error => {
// Reject promise if usual error
if (error.response.status !== 401) {
return Promise.reject(error);
}
/*
* When response code is 401, try to refresh the token.
* Eject the interceptor so it doesn't loop in case
* token refresh causes the 401 response
*/
axios.interceptors.response.eject(interceptor);
return axios.post('/api/refresh_token', {
'refresh_token': this._getToken('refresh_token')
}).then(response => {
saveToken();
error.response.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return axios(error.response.config);
}).catch(error => {
destroyToken();
this.router.push('/login');
return Promise.reject(error);
}).finally(createAxiosResponseInterceptor);
}
);
}
推荐阅读
- vba - 使用硒从 VBA 中的 Css 元素中获取价值
- objective-c - Mac Catalyst 中的鼠标滚动
- discord - discord.js 在 oauth2 之后获取用户的所有行会
- java - 如何将 Spring Boot 安全 API 连接到 Android 应用程序
- python - 具有月份频率的熊猫日期范围
- javascript - Heroku 服务器上的 CORS 策略问题
- twitter-bootstrap - 如何在 Bootstrap 网格中创建 v-for 循环?
- python - Flask-将数据从数据库保存到列表
- python - 在python pandas中创建按周滚动分组的列的滚动总和
- javascript - 如何修复类型无效 - 期望字符串(用于内置组件)或 react.js 中的类/函数?