React router v4 - 具有HOC的授权路由

时间:2017-09-23 13:09:33

标签: javascript reactjs functional-programming react-router-v4 higher-order-components

我遇到问题,以防止未经授权的用户访问授权的路由/组件 - 例如登录用户信息中心

我有以下代码:

import React from 'react'
//other imports
import {withRouter} from 'react-router'

class User extends React.Component {
  constructor(props) {
    super(props)
    console.log('props', props)
    let user = JSON.parse(localStorage.getItem('userDetails'))
    if(!user || !user.user || props.match.params.steamId !== user.user.steamId) {
      props.history.push('/')
    } else {
      this.props.updateUserState(user)
      this.props.getUser(user.user.steamId)
    }
  }

  //render function
}

//mapStateToProps and mapDispatchToProps

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(User))

路由器:

render() {
    return (
      <Router>
        <div>
          <Route exact path="/" component={Main}/>
          <Route path="/user/:steamId" component={User}/>
          <Route path="/completelogin" component={CompleteLogin}/>
        </div>
      </Router>
    )
  }

我尝试登录以检查是否输入了条件,但是我从渲染函数中得到一个错误,说它无法读取null的属性。

有没有办法解决我的问题,也有更好的方法来满足我的要求?只有授权用户才能严格访问特定组件

2 个答案:

答案 0 :(得分:8)

警告:以下答案使用React的旧上下文API。如果您使用的是V16.3 +,则以下答案不适用于您

好的,所以,根据您的逻辑,未经授权的用户被禁止访问用户组件。简单而公平。没问题。

但我担心的是,您是否正在检查用户是否已登录未经身份验证的用户不应进入的组件。我认为这是不正确的,因为:

  1. 这是我们计划的额外旅程 - 增加了额外的不必要的低效率。有可能从路由器我们转到用户组件,后者将我们发送回前者。乒乓球。

  2. 用户组件看起来很脏。有无关的逻辑。是无关紧要的。因为不应在用户组件中进行身份验证检查。用户组件应包含用户相关的东西。

  3. 您认为如果不是进入用户组件来检查用户身份验证,我们会在路由器中进行检查吗?用户组件,如其名称所述,专用于用户,并且应该从那里取出检查身份验证的逻辑。

    好的,多酷。但是如何?

    我们可以创建一个Higher-Order Component (HOC)作为参数,将任何组件传递给它。然后,我们在HOC中添加身份验证逻辑,最后,根据我们使用的逻辑,我们可以重定向到主页或允许请求到给定组件。

    为了使HOC能够执行上述操作,需要访问:

    1. 状态。我们需要知道用户是否已登录,状态是我们存储此类数据的位置。
    2. 路由器。我们可能需要重定向用户。
    3. 让我们将HOC命名为required_auth。这是它的代码:

      import React, { Component } from 'react';
      import { connect } from 'react-redux';
      
      export default function(ComposedComponent) {
          class Authentication extends Component {
              static contextTypes = {
                  router: React.PropTypes.object
              }
      
              componentWillMount() {
                  if (!this.props.authenticated) {
                      this.context.router.history.push('/');
                  }
              }
      
              componentWillUpdate(nextProps) {
                  if (!nextProps.authenticated) {
                      this.context.router.history.push('/');
                  }
              }
      
              render() {
                  return <ComposedComponent {...this.props} />
              }
          }
      
          function mapStateToProps(state) {
              return { authenticated: state.auth.authed };
          }
      
          return connect(mapStateToProps)(Authentication);
      }
      

      正如你所看到的,这里没有黑魔法发生。可能令人困惑的是

      static contextTypes = {
          router: React.PropTypes.object
      }
      
        

      contextprops类似,但它允许我们跳过   我们的组件层次结构中的级别

      由于this.context非常容易访问和滥用,React迫使我们以这种方式定义上下文。

      不要使用上下文,除非您真的知道自己在做什么。上下文的用例并不常见。详细了解后果可能是here

      总结一下我们的HOC,它只是将一个组件作为参数,它可以重定向到主页,也可以返回我们传递给它的组件。

      现在使用它,在路线文件中导入HOC

       import RequiredAuth from './components/auth/required_auth';
      

      以及我们想要保护免受非授权用户的任何路由,我们只需这样路由:

      <Route path="/user" component={RequiredAuth(User)}/>
      

      上面的行将指向主页或返回我们传递的组件User

      参考文献: https://facebook.github.io/react/docs/higher-order-components.html https://facebook.github.io/react/docs/context.html

答案 1 :(得分:2)

由于您使用的是React Router 4,Matthew Barbara的答案虽然绝对正确,但却不必要地复杂化。使用RR4,您只需使用Redirect组件来处理重定向。有关详细信息,请参阅https://reacttraining.com/react-router/web/api/Redirect

import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";

export default function requireAuth (WrappedComponent) {

  class Authentication extends Component {

    render () {

      if (!this.props.authenticated) {
        return <Redirect to="/"/>
      }

      return <WrappedComponent { ...this.props }/>
    }
  }

  function mapStateToProps (state) {
    return { authenticated: state.authenticated }
  }

  return connect(mapStateToProps)(Authentication);
}