从cookie异步加载身份验证令牌时,React react-router-dom私有路由不起作用

Posted

技术标签:

【中文标题】从cookie异步加载身份验证令牌时,React react-router-dom私有路由不起作用【英文标题】:React react-router-dom private route not working when auth token is asynchronously loaded from cookies 【发布时间】:2020-10-18 08:56:54 【问题描述】:

我有一个 react/redux 应用程序,使用“react-router-dom”进行路由。我在我的 App.js 中使用以下内容(标签是 react-bootstrap,以提供私有路由。当且仅当用户未登录时,预期行为将被重定向到 /login(基于cookie)。实际行为是应用程序立即重定向到/login,即使用户已登录。

class MainContainer extends Component 
  constructor(props) 
    super(props);
    this.props.loadAuthCookie();
  

  PrivateRoute = ( component: ChildComponent, ...rest ) => 
    return (
      <Route
        ...rest
        render=(props) => 
          if (!this.props.auth.loggedIn && !this.props.auth.authPending) 
            return <Redirect to="/login" />;
           else 
            return <ChildComponent ...props />;
          
        
      />
    );
  ;

  render() 
    const  PrivateRoute  = this;
    return (
      <Router>
        <Container fluid id="root">
          <Header />
          <Switch>
            <Row className="pageContainer">
              <Col>
                <PrivateRoute exact path="/" component=HomeScreen />
                <Route
                  exact
                  path="/clusters/"
                  component=clusterOverviewScreen
                />
                <Route exact path="/login" component=LoginPage />
              </Col>
            </Row>
          </Switch>
        </Container>
      </Router>
    );
  


const mapStateToProps = (state) => 
  return 
    auth: state.auth,
  ;
;

const mapDispatchToProps = (dispatch) => 
  return 
    loadAuthCookie: () => 
      return dispatch(loadAuthCookie());
    ,
  ;
;

const RootContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(MainContainer);

export default class App extends Component 
  render() 
    return (
      <Provider store=store>
        <RootContainer />
      </Provider>
    );
  

this.props.loadAuthCookie 函数调度一个 redux 操作,该操作拉取包含身份验证令牌(如果存在)的 cookie,并将其置于状态。 初始状态是


    "authPending": false
    "loggedIn": false
    "authToken": null

变成:


    "authPending": true
    "loggedIn": false
    "authToken": null

最后:


    "authPending": false
    "loggedIn": true
    "authToken": TOKEN

我对反应比较陌生。我的猜测是,当 PrivateRoute 函数运行时,redux action 还没有设置状态,因此,PrivateRoute 组件重定向到登录页面。这个猜测是基于在私有路由中记录 props.auth.loggedIn - 这会返回 false,但是当我重定向到的登录页面完成加载时,组件道具状态 this.props.loggedIn 是 true。我不知道如何不过要解决这个问题。

编辑:loadAuthCookie 操作:

export const loadAuthCookie = () => (dispatch) => 
  dispatch(AuthReducer.setAuthPending());
  try 
    const cookie = Cookies.get("token");
    if (cookie) 
      dispatch(AuthReducer.setLoginSuccess(cookie));
    
   catch (err) 
    console.log(err);
    dispatch(AuthReducer.setLogout());
  
;

【问题讨论】:

你能不能也分享一下你的 loadAuthCookie 动作函数? 尝试将this.props.loadAuthCookie();作为render的第一行而不是在构造函数中;之前const PrivateRoute = this; @Hyetigran 完成。该建议似乎导致最大深度超出错误 这是因为函数在构造函数中被调用,然后被重新渲染并再次调用函数。只调用一次函数,应该使用componentDidMount生命周期方法componentDidMount() this.props.loadAuthCookie() 【参考方案1】:

PrivateRoute 看起来执行不正确。

它应该被设计为“等待”直到authPending 变为falseloggedIn 变为true

但实际上,它要求它们都为真,才能不重定向到登录。因此,如果authPending: true 直到loggedIn:true,则实施“等待”。

这是结果表。

也不要使用像!loggedIn &amp;&amp; !authPending 这样可读性很差的条件,尤其是当你一直在工作时。我已经看过这个了,因为我无法被要求理解我所看到的。

<Route
    ...rest
    render=(props) => 
        if (this.props.auth.loggedIn) 
            return <ChildComponent ...props />;
         else if (this.props.auth.authPending) 
            return <LoadingScreen/> // Or could simply div with text. Not different component
            // Then state updates after x seconds and re-renders
         else 
            return <Redirect to="/login" />;
        
    
/>

编辑:实际上我的逻辑错了,authPending &amp; loggedIn 可以是 true 重定向到子元素,因为 ! 逆运算符。

function test(first, second) 
    if (!first && !second) 
        console.log('login');
     else 
        console.log('child');
        
    


test(true, true);
test(false,false);
test(true, false);
test(false, true);

输出:

child
login
child
child

看到还是想不通!;

【讨论】:

【参考方案2】:

我认为托尼是对的;您可能需要在此组件的生命周期后期调用this.props.loadAuthCookie();

构造函数在(且仅当)组件被实例化时运行;而不是在安装或渲染时。构造函数通常在挂载或渲染之前调用一次。

因此,调用 this.props.loadAuthCookie() 也可能在 componentDidMount 内部工作,但我会接受 Tony 的建议并在 render() 函数中尝试。

【讨论】:

感谢您的建议,但这会触发最大更新深度超出错误。如果有帮助,我已将我的 loadAuthCookie 代码添加到问题中。 听起来this.props.loadAuthCookie() 正在强制渲染(在渲染期间)创建这个无限的渲染循环。需要尝试的几件事: 1. 如果您将this.props.loadAuthCookie(); 移动到componentDidMount() ,是否会发生“最大更新深度”错误? 2. 您可能可以在调用this.props.loadAuthCookie() 之前放置一个保护,这样当auth cookie 已经填充时,它会在以后的渲染中跳过该函数。例如,将调用 loadAuthCookie 包装在条件中,这样它只会在 cookie 不存在时触发:if (!this.props.auth.loggedIn) ... 不,如果我将它移动到 componentDidMount,它不会发生,但它表现出与以前相同的行为。添加一个守卫(在渲染中或在 componentDidMount 中)使其工作,但如果应用程序不重新渲染。如果我点击指向 privateRoute 的链接,它会呈现,但是,如果我直接访问 url(即导致应用重新呈现),它会不断重定向到 /login 我不熟悉在使用它的组件中实例化高阶组件的模式。您可以尝试将 PrivateRoute 移出 MainComponent 类(最好移到单独的文件中,在 MainComponent 文件中使用 export default PrivateRouteimport PrivateRoute from './privateRoute.ts' 之类的东西)...这可能会使控制流和应用程序状态更容易推理。

以上是关于从cookie异步加载身份验证令牌时,React react-router-dom私有路由不起作用的主要内容,如果未能解决你的问题,请参考以下文章

通过 cookie 标头发送令牌身份验证信息是不是安全?

在 React Redux 应用程序中使用 JWT 身份验证令牌的正确方法

从多页应用程序进行身份验证后,如何在初始下载期间将 JWT 令牌加载到 React?

在 Chromecast Receiver 中利用基于 cookie 的身份验证

您是不是更改 cookie 身份验证用户的身份验证令牌?如果有,多久一次?

如果用于身份验证的 JWT 令牌保存在 HTTP-Only cookie 中,您如何从 cookie 中读取它以便我可以将其包含在请求标头中?