cho*_*bo2 7 authentication mbox reactjs axios refresh-token
我正在使用reactjs,mbox和axios并遇到问题。我有一个提供访问令牌和刷新令牌的api。访问令牌每20分钟消失一次,当服务器发回401消息时,我的代码将自动发送刷新令牌以获取新的访问令牌。
授予新的访问令牌后,将再次发送相同的拒绝请求。现在,我的代码运行良好,直到我抛出多个拒绝,这几乎可以同时触发所有拒绝。
因此,第一个请求关闭,发送回一个401,它获得了一个新的刷新令牌,其他所有请求都将尝试执行相同的操作,但是其他请求现在将失败,因为将使用刷新令牌和一个新的令牌将发出第一个请求。
这将启动我的代码以将用户重定向到登录页面。
所以从本质上讲,我一次只能收到1个请求。
export const axiosInstance = axios.create({
baseURL: getBaseUrl(),
timeout: 5000,
contentType: "application/json",
Authorization: getAuthToken()
});
export function updateAuthInstant() {
axiosInstance.defaults.headers.common["Authorization"] = getAuthToken();
}
function getAuthToken() {
if (localStorage.getItem("authentication")) {
const auth = JSON.parse(localStorage.getItem("authentication"));
return `Bearer ${auth.accessToken}`;
}
}
axiosInstance.interceptors.response.use(
function(response) {
return response;
},
function(error) {
const originalRequest = error.config;
if (error.code != "ECONNABORTED" && error.response.status === 401) {
if (!originalRequest._retry) {
originalRequest._retry = true;
return axiosInstance
.post("/tokens/auth", {
refreshToken: getRefreshToken(),
grantType: "refresh_token",
clientId : "myclient"
})
.then(response => {
uiStores.authenticaionUiStore.setAuthentication(JSON.stringify(response.data))
updateAuthInstant();
return axiosInstance(originalRequest);
});
} else {
uiStores.authenticaionUiStore.logout();
browserHistory.push({ pathname: '/login',});
}
}
return Promise.reject(error);
}
);
Run Code Online (Sandbox Code Playgroud)
编辑
我有一个问题,当用户在直接网址中复制时,我需要检查以进行身份验证的代码无法正常工作
app.js
<React.Fragment>
<Switch>
<Route path="/members" component={MemberAreaComponent} />
</Switch>
</React.Fragment >
Run Code Online (Sandbox Code Playgroud)
在memberAreaComponent中
<Route path="/members/home" component={MembersHomeComponent} />
Run Code Online (Sandbox Code Playgroud)
当我输入 http://www.mywebsite/members/home
MembersHomeComponent - componentDidMount runs first
MemberAreaComponent - componentDidMount runs second
AppCoontainer = componentDidMount runs last.
Run Code Online (Sandbox Code Playgroud)
Hi I have implemented same scenario in react/redux app. But it would help you to achieve the goal. You don't need to check 401 in each API call. Just implement it in your first validation API request. You can use setTimeOut to send refresh token api request before some time of authentication token expiry. So locatStorage will get updated and All axios requests won't get expired token ever. Here is my solution:
in my Constants.js I;m maintaining USER TOKEN in localStorage like this:
export const USER_TOKEN = {
set: ({ token, refreshToken }) => {
localStorage.setItem('access_token', token);
localStorage.setItem('refresh_token', refreshToken);
},
remove: () => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
},
get: () => ({
agent: 'agent',
token: localStorage.getItem('access_token'),
refreshToken: localStorage.getItem('refresh_token'),
}),
get notEmpty() {
return this.get().token !== null;
},
};
export const DEFAULT_HEADER = {
get: () => ({
'Content-type': 'application/json;charset=UTF-8',
agent: `${USER_TOKEN.get().agent}`,
access_token: `${USER_TOKEN.get().token}`,
}),
};
Run Code Online (Sandbox Code Playgroud)
on page load, User Validate API request is as follows:
dispatch(actions.validateUser(userPayload)) // First time authentication with user credentials and it return access token, refresh token and expiry time
.then(userData => {
const { expires_in, access_token, refresh_token } = userData
USER_TOKEN.set({ // setting tokens in localStorage to accessible to all API calls
token: access_token,
refreshToken: refresh_token,
});
const timeout = expires_in * 1000 - 60 * 1000; // you can configure as you want but here it is 1 min before token will get expired
this.expiryTimer = setTimeout(() => { // this would reset localStorage before token expiry timr
this.onRefreshToken();
}, timeout);
}).catch(error => {
console.log("ERROR", error)
});
onRefreshToken = () => {
const { dispatch } = this.props;
const refresh_token = USER_TOKEN.get().refreshToken;
dispatch(actions.refreshToken({ refresh_token })).then(userData => {
const { access_token, refresh_token } = userData
USER_TOKEN.set({
token: access_token,
refreshToken: refresh_token,
});
});
};
Run Code Online (Sandbox Code Playgroud)
Feel free to ask any questions, The other way is to implement axios abort controller to cancel pending promises. Happy to help with that too !
EDITED - You can maintain axios token source in all you API requests to abort them anytime. maintain axios token source in all of your apis. once you get first promise resolved then you can cancel all other pending APIs request. You can invoke onAbort method in after your first promise gets resolved. See this:
//in your component
class MyComponent extends Component{
isTokenSource = axios.CancelToken.source(); // a signal you can point to any API
componentDidMount{
// for example if you're sending multiple api call here
this.props.dispatch(actions.myRequest(payload, this.isTokenSource.token))
.then(() => {
// all good
})
.catch(error => {
if (axios.isCancel(error)) {
console.warn('Error', error);
}
});
}
onAbortStuff = () => { // cancel request interceptor
console.log("Aborting Request");
this.isTokenSource.cancel('API was cancelled'); // This will abort all the pending promises if you send the same token in multiple requests,
}
render(){
//
}
Run Code Online (Sandbox Code Playgroud)
While in your axios request you can send token like this:
export const myRequest= (id, cancelToken) => {
const URL = `foo`;
return axios(URL, {
method: 'GET',
headers: DEFAULT_HEADER.get(),
cancelToken: cancelToken
})
.then(response => {
// handle success
return response.data;
})
.catch(error => {
throw error;
});
};
Run Code Online (Sandbox Code Playgroud)
For reference you can this article it is very helpful in understanding of cancel subscriptions. https://medium.freecodecamp.org/how-to-work-with-react-the-right-way-to-avoid-some-common-pitfalls-fc9eb5e34d9e
You can do your routes structuring in this way: index.js
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
Run Code Online (Sandbox Code Playgroud)
App.js:
class App extends Component {
state = {
isAuthenticated: false,
};
componentDidMount() {
//authentication API and later you can setState isAuthenticate
}
render() {
const { isAuthenticated } = this.state;
return isAuthenticated ? <Routes /> : <Loading />;
}
Run Code Online (Sandbox Code Playgroud)
If you still find any issue, I'm more than happy to help you with this.
| 归档时间: |
|
| 查看次数: |
4145 次 |
| 最近记录: |