来自 React/Redux 应用程序中组件的权限检查

IT技术 javascript reactjs ecmascript-6 redux react-redux
2021-05-11 00:06:12

我正在尝试与构建 React 应用程序的团队合作,并试图找出创建“高阶”React 组件(一个包装另一个)以结合 Redux 数据存储执行身份验证的最佳方法。

到目前为止,我的方法是创建一个module,该module由一个函数组成,该函数根据是否有经过身份验证的用户返回一个新的 React 组件。

export default function auth(Component) {

    class Authenticated extends React.Component {

        // conditional logic

        render(){
            const isAuth = this.props.isAuthenticated;

            return (
                <div>
                    {isAuth ? <Component {...this.props} /> : null}
                </div>
            )
        }
    }

    ...

    return connect(mapStateToProps)(Authenticated);

}

这使我团队中的其他人可以轻松指定组件是否需要某些权限。

render() {
    return auth(<MyComponent />);
}

如果您正在执行基于角色的检查,这种方法是有意义的,因为您可能只有几个角色。在这种情况下,您只需调用auth(<MyComponent />, admin).

对于基于权限的检查,传递参数变得笨拙。然而,在构建组件时在组件级别指定权限可能是可行的(以及在团队环境中可管理)。设置静态方法/属性似乎是一个不错的解决方案,但据我所知,es6 类导出为函数,不会显示可调用方法。

有没有办法访问导出的 React 组件的属性/方法,以便可以从包含的组件访问它们?

4个回答

这似乎是一个非常有趣的可能性。我在谷歌搜索相同的问题时遇到了这个问题,这是一个新的库,所以我认为链接它不会有什么坏处,以防其他人可以得到它的帮助。我还没有决定是否要自己走这条路,因为我已经 15 分钟进入 Google-palooza。它被称为CASL。

链接到解释图书馆的文章

链接到图书馆

每个请求的库中的示例代码:

if (ability.can('delete', post)) {
  <button onClick={this.deletePost.bind(this}>Delete</button>
}

替换如下内容:

if (user.role === ADMIN || user.auth && post.author === user.id) {
  <button onClick={this.deletePost.bind(this}>Delete</button>
}

作者在文章中进一步使用自定义组件获得:

<Can run="delete" on={this.props.todo}>
  <button onClick={this.deleteTodo.bind(this}>Delete</button>
</Can>

它基本上允许开发人员在他们的代码中更具声明性,以便于使用和维护。

onEnter很棒,在某些情况下很有用。但是,这里有一些 onEnter 没有解决的常见认证和授权问题:

  • 从 redux 存储数据决定身份验证/授权(有一些解决方法
  • 如果商店更新(但不是当前路线),则重新检查身份验证/授权

  • 如果受保护路由下的子路由发生更改,则重新检查身份验证/授权

另一种方法是使用高阶组件。

您可以使用 Redux-auth-wrapper提供高阶组件,以便于阅读和应用您的组件的身份验证和授权约束。


  • 要获取子方法,您可以使用:refs, callback and callback from refs

  • 要获得儿童props,您可以使用:this.refs.child.props.some or compInstance.props.some

方法和props示例:

class Parent extends Component {
    constructor(props){
        super(props);
        this.checkChildMethod=this.checkChildMethod.bind(this);
        this.checkChildMethod2=this.checkChildMethod2.bind(this);
        this.checkChildMethod3=this.checkChildMethod3.bind(this);
    }
    checkChildMethod(){
        this.refs.child.someMethod();
        console.log(this.refs.child.props.test);
    }
    checkChildMethod2(){
        this._child2.someMethod();
        console.log(this._child2.props.test);
    }
    checkChildMethod3(){
        this._child3.someMethod();
        console.log(this._child3.props.test);
    }
    render(){
        return (
            <div>
                Parent
                <Child ref="child" test={"prop of child"}/>
                <ChildTwo ref={c=>this._child2=c} test={"prop of child2"}/>
                <ChildThree returnComp={c=>this._child3=c} test={"prop of child3"}/>
                <input type="button" value="Check method of child" onClick={this.checkChildMethod}/>
                <input type="button" value="Check method of childTwo" onClick={this.checkChildMethod2}/>
                <input type="button" value="Check method of childThree" onClick={this.checkChildMethod3}/>
            </div>
        );
    }
}

class Child extends Component {
    someMethod(){
        console.log('someMethod Child');
    }
    render(){
        return (<div>Child</div>);
    }
}
class ChildTwo extends Component {
    someMethod(){
        console.log('someMethod from ChildTwo');
    }
    render(){
        return (<div>Child</div>);
    }
}
class ChildThree extends Component {
    componentDidMount(){
        this.props.returnComp(this);
    }
    someMethod(){
        console.log('someMethod from ChildThree');
    }
    render(){
        return (<div>Child</div>);
    }
}

如果使用react-router,推荐的处理授权方式是通过组件中onEnter属性Route

<Route path="/" component={Component} onEnter={Component.onEnter} />  

请参阅文档

这也是对您问题的回答:

有没有办法访问导出的 React 组件的属性/方法,以便可以从包含的组件访问它们?

因此,只需将它们设为静态属性/方法(如 Component.onEnter)。

我在这里找到了一篇文章,其中的要点是我在这里写的。你可以像这样向你的组件添加一个props

<Route path="/" component={App}>

//BOD routes
<Route authorisedUsers={['KR']} path="/home" component={HomeContainer} />

//HR routes
<Route authorisedUsers={['HR']} path="/hrhome" component={HRDashboardContainer} />

//common routes    
<Route authorisedUsers={['KR', 'HR']} path="/notes" component={NotesContainer} />

然后在你的组件中添加以下代码,在 path='/' 上呈现

Role based routing react redux
componentDidUpdate() {
  const { 
      children,  //Component to be rendered (HomeContainer if route = '/home')
      pathname: {location},  //location.pathname gives us the current url user is trying to hit. (with react router)
      profileId, //profileId required by profile page common to all user journeys.
      role } = this.props; 
  this.reRoute(role, this.props.children, location.pathname, ProfileId)
}

decideRoute(role, ProfileId) { //decide routes based on role
  if(role==='HR')
    return 'hrhome';
  else if(role==='KR')
    return 'home';
  else if(role==='USER'&&ProfileId)
    return 'profile/'+ProfileId;
  else
  return '/error';
}

isAuthorised(authorisedUsers, role) {
  return _.includes(authorisedUsers, role)
}

reRoute(role, children, path, ProfileId) {
  if(role===null && path!=='/') // user hit a different path without being authenticated first
  {
    hashHistory.replace('/');  //this is where we implemented login
    return;
  }
  let route = this.decideRoute(role, ProfileId)  //if role has already been fetched from the backend, use it to decide the landing page for each role.
  if(children)  // if we already are on one of the user journey screens ...
  {
    const authorisedUsers = children.props.route.authorisedUsers 
    if(!this.isAuthorised(authorisedUsers,role)) //... and the user is not allowed to view this page...
    hashHistory.replace(`/${route}/`);  //... redirect him to the home page corresponding to his role.
  }
  else
  hashHistory.replace(`/${route}/`);  // if the user has just logged in(still on / page or login page), and we know his role, redirect him to his home page.
}//if none of these holds true, user is allowed to go ahead and view the page

这本质上添加了一个网关检查,该检查适用于您的所有容器,并将根据您的角色指导您。此外,如果您以某种方式打错了网址,它也不会允许您访问。