Javascript axios 拦截器和异步登录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35900230/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Axios interceptors and asynchronous login
提问by Dmitry Shvedov
I'm implementing token authentication in my web app. My access token
expires every N minutes and than a refresh token
is used to log in and get a new access token
.
我正在我的网络应用程序中实现令牌身份验证。Myaccess token
每 N 分钟过期一次,然后refresh token
使用a登录并获取新的access token
.
I use Axios for all my API calls. I have an interceptor set up to intercept 401
responses.
我所有的 API 调用都使用 Axios。我设置了一个拦截器来拦截401
响应。
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => { setTokens(success.access_token, success.refresh_token) },
error => { console.log('Refresh login error: ', error) }
)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken()
return axios(err.config);
}
throw err
})
Basically, as I intercept a 401 response, I want to do a login and than retry the original rejected request with the new tokens. My serviceRefreshLogin
function calls setAccessToken()
in its then
block. But the problem is that
the then
block happens later than the getAccessToken()
in the interceptor, so the retry happens with the old expired credentials.
基本上,当我拦截 401 响应时,我想进行登录,然后使用新令牌重试原始被拒绝的请求。我的serviceRefreshLogin
函数setAccessToken()
在其then
块中调用。但问题是该then
块发生的时间晚于getAccessToken()
拦截器中的 ,因此重试发生在旧的过期凭据上。
getAccessToken()
and getRefreshToken()
simply return the existing tokens stored in the browser (they manage localStorage, cookies, etc).
getAccessToken()
并getRefreshToken()
简单地返回存储在浏览器中的现有令牌(它们管理 localStorage、cookies 等)。
How would I go about ensuring statements do not execute until a promise returns?
我将如何确保语句在承诺返回之前不会执行?
(Here's a corresponding issue on github: https://github.com/mzabriskie/axios/issues/266)
(这是github上的相应问题:https: //github.com/mzabriskie/axios/issues/266)
回答by ForceUser
Just use another promise :D
只需使用另一个承诺:D
axios.interceptors.response.use(undefined, function (err) {
return new Promise(function (resolve, reject) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => {
setTokens(success.access_token, success.refresh_token)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
axios(err.config).then(resolve, reject);
},
error => {
console.log('Refresh login error: ', error);
reject(error);
}
);
}
throw err;
});
});
If your enviroment doesn't suport promises use polyfill, for example https://github.com/stefanpenner/es6-promise
如果您的环境不支持 promise,请使用 polyfill,例如https://github.com/stefanpenner/es6-promise
But, it may be better to rewrite getRefreshToken to return promise and then make code simpler
但是,最好重写 getRefreshToken 以返回承诺,然后使代码更简单
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
return getRefreshToken()
.then(function (success) {
setTokens(success.access_token, success.refresh_token) ;
err.config.__isRetryRequest = true;
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
return axios(err.config);
})
.catch(function (error) {
console.log('Refresh login error: ', error);
throw error;
});
}
throw err;
});
回答by Daniel
Could do it in the request instead of the response, and it'd probably be cleaner since it'd avoid hitting the server when the access token's expired. Copying from this article:
可以在请求而不是响应中执行此操作,并且它可能会更清晰,因为它会在访问令牌过期时避免访问服务器。从这篇文章复制:
function issueToken() {
return new Promise((resolve, reject) => {
return client({
...
}).then((response) => {
resolve(response);
}).catch((err) => {
reject(err);
});
});
}
client.interceptors.request.use((config) => {
let originalRequest = config;
if (tokenIsExpired && path_is_not_login) {
return issueToken().then((token) => {
originalRequest['Authorization'] = 'Bearer ' + token;
return Promise.resolve(originalRequest);
});
}
return config;
}, (err) => {
return Promise.reject(err);
});