我在项目的前端使用Axios,并且需要一种将用户重定向到登录页面的方式,以防他想使用过期的令牌(刷新令牌对后端无效),我发现了{ 3}}在网络上看起来很容易适应我的技术堆栈(没有Redux的ReactJS):
import axios from 'axios';
// additional code that lives in its own module
const getToken = () => {
return isAuth() ? window.localStorage.getItem("token") : "";
};
const getRefreshToken = () => {
return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};
const setTokens = (token, refresh) => {
window.localStorage.setItem("token", token);
window.localStorage.setItem("refresh_token", refresh);
};
// this is on my app.js file
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
window.location.pathname = "/login";
});
}
return Promise.reject(error);
});
问题在于代码进入了无限递归状态,从而使前端无法使用。如果我手动导航到/login
,则递归将完全停止。
这是文件的整个源代码,我只需要根据我的问题接受的解决方案进行更改即可:
import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';
import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';
import axios from 'axios';
const myhistory = createHistory();
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
myhistory.replace("/login");
});
}
return Promise.reject(error);
});
class App extends Component {
render() {
return (
<Router history={myhistory}>
<Fero/>
</Router>
);
}
}
export default App;
答案 0 :(得分:2)
这是个好问题,我对axios拦截器一无所知。无论如何,我找到了解决该问题的方法。最主要的是,您必须将拦截器设置包装在一个函数中,以便可以将历史对象作为参数传递。 您需要历史对象。一种可能的解决方案是创建一个并向路由器以及包装好的拦截器设置提供相同的历史对象。
// in app.js create a history
const history = createHistory();
// with the history object, setup your interceptor
setupInterceptor(history)
// you have to use the same history object in your
// Router component, if it is in a different component
// you could pass it in a prop
<Router history={history}>
...
</Router>
const setupInterceptor = (history) => {
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
// ...
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
history.push("/login");
});
}
return Promise.reject(error);
})};
答案 1 :(得分:1)
好吧,事实证明,递归的解决方案是使用axios的不同实例进行令牌刷新,因此拦截器没有拦截到过期的刷新令牌的HTTP 401响应,这是完整的解决方案:
import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';
import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';
import axios from 'axios';
const myhistory = createHistory();
const refresh = axios.create();
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && Boolean(getRefreshToken())) {
// Hace la solicitud de refresco de tokens
return refresh.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch((error) => {
setTokens("", "");
myhistory.replace("/login");
});
}
return Promise.reject(error);
});
class App extends Component {
render() {
return (
<Router history={myhistory}>
<Fero/>
</Router>
);
}
}
export default App;
src/utils/auth.js
的内容:
export const isAuth = () => {
return window.localStorage.getItem("token") && window.localStorage.getItem("refresh_token");
};
export const getToken = () => {
return isAuth() ? window.localStorage.getItem("token") : "";
};
export const getRefreshToken = () => {
return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};
export const setTokens = (token, refresh) => {
window.localStorage.setItem("token", token);
window.localStorage.setItem("refresh_token", refresh);
};