路由跳转前验证Token:异步请求导致页面无法正常跳转的解决
我的React路由鉴权问题及解决方案
问题背景
我本来以为这是个简单问题,但对异步/回调逻辑不太熟悉。我写了PrivateRoute组件来保护后台资源,防止未认证用户直接访问。最开始只检查本地存储里的token,正常情况下能跳转到资源页,但没法处理token过期的情况——用户本地存着过期token,系统还是会让他进资源页,这显然不安全。所以我想在跳转前先调用后端接口验证token的有效性。
遇到的问题
axios请求能正确返回结果,console.log也显示then块执行了,但已认证用户就是没法跳转到对应资源页,页面一直停留在登录页。
原App.js代码
const PrivateRoute = ({component: Component, isAuthorized, ...otherProps}) => ( <Route {...otherProps} render={props => ( isAuthorized() ? (<Component {...props} />) : ( <Redirect to={ { pathname: '/', state: {from: props.location}, } } /> ) )} /> ); function verifyToken(isAuthenticated){ if (isAuthenticated) { axios.post(`${BACKEND_URL}/api-token-verify/`, { token: getAuthToken() }) .then((res) => { console.log('then'); return true; }) .catch((err) => { console.log('err'); return false; }); } else { console.log('ahaha'); return false; } } // Deal with an ordinary outdated token. Hacked one will be handle on individual component function hasToken() { const token = localStorage.getItem('authToken'); const isAuthenticated = !((token === undefined) | (token === null)); return verifyToken(isAuthenticated); } class App extends Component { render() { return ( <Provider store={store}> <BrowserRouter> <Switch> <Route exact path='/' component={Login}/> <PrivateRoute exact path='/simulator/' isAuthorized={hasToken} component={Simulator}/> </Switch> </BrowserRouter> </Provider> ) } } export default App;
我想知道怎么才能让异步验证的流程按预期顺序执行?
解决方案
感谢m0meni的回答和评论帮我搞定了这个问题。我基于他的方案修改了代码,因为没法在顶层App组件里直接用Redux处理异步逻辑,所以新建了Container组件来托管所有路由和鉴权逻辑。
修改后的App.js
class App extends Component { render() { return ( <Provider store={store}> <Container/> </Provider> ) } }
Container.js代码
import React, {Component, Fragment} from 'react'; import {BrowserRouter, Redirect, Route, Switch} from 'react-router-dom'; import Simulator from "../../simulators/components/Simulators"; import Login from "../../frontpage/components/login"; import {connect} from 'react-redux'; import {CHECK_TOKEN} from "../../constants"; const PrivateRoute = ({component: Component, isAuthorized, ...otherProps}) => ( <Route {...otherProps} render={props => ( isAuthorized() ? (<Component {...props} />) : ( <Redirect to={ { pathname: '/', state: {from: props.location}, } } /> ) )} /> ); // Deal with an ordinary outdated token. Hacked one will be handle on individual component function hasToken() { const token = localStorage.getItem('authToken'); return !((token === undefined) | (token === null)); } class Container extends Component { /* * 为了处理Redux相关逻辑,同时不打乱顶层App组件的结构,我新建了Container组件来统管所有内容 * */ constructor(props) { super(props); this.props.validateToken(); } render() { console.log(this.props); const {isAuthenticated, wrapper} = this.props; console.log(typeof this.props.wrapper); if((typeof this.props.wrapper) === 'function') { console.log('this is function'); return ( <Fragment> <BrowserRouter> <Switch> <Route exact path='/' component={Login}/> <PrivateRoute exact path='/simulator/' isAuthorized={this.props.wrapper} component={Simulator}/> </Switch> </BrowserRouter> </Fragment> ) }else{ console.log('wrapper is not a function'); console.log(typeof this.props.wrapper); return null; } } } const mapStateToProps = ({verifyTokenReducer}, ownProps) => { return verifyTokenReducer }; const validateToken = () => { return { type: CHECK_TOKEN, payload: undefined } }; export default connect(mapStateToProps, {validateToken})(Container);
reducers.js代码
import {GOOD_TOKEN, INVALID_TOKEN} from "../constants"; function immediateReturnTrue(){ return true; } function immediateReturnFalse(){ return false; } export const VerifyTokenReducer = (state = {}, action) => { switch (action.type) { case GOOD_TOKEN: return { isAuthenticated: true, wrapper: immediateReturnTrue }; case INVALID_TOKEN: return { isAuthenticated: false, wrapper: immediateReturnFalse }; default: return state; } };
内容的提问来源于stack exchange,提问作者joe
相关产品推荐
相关产品推荐

