我创建了一个高阶组件WithAccess
,以检查用户是否具有进入某些受保护路线的正确权限(登录和帐户角色)。我正在这样使用hoc:
const authCondition = authUser => !!authUser;
<Route path="/a" component={WithAccess(authCondition, "admin")(Admin)} />
<Route path="/u" component={WithAccess(authCondition, "user")(User)} />
Admin和User是具有路由的两个功能组件。
WithAccess包含一个onAuthStateChanged侦听器。在侦听器内部,我正在检查用户的角色(在创建用户时,我将自定义声明设置为“角色”)。如果这与向下传递的属性“角色”匹配,则isLoading设置为false,组件将呈现。否则,用户将被重定向回到登录页面。
const WithAccess = (condition, role) => Component => {
class withAccess extends React.Component {
state = {
isLoading: true
};
componentDidMount() {
Auth.onAuthStateChanged(async user => {
if (user) {
const idTokenResult = await user.getIdTokenResult(true);
if (condition(user) && idTokenResult.claims.role === role)
this.setState({ isLoading: false });
else this.props.history.push(`/login`);
}
});
}
render() {
return !this.state.isLoading ? (
<Component />
) : (
<PageLoader label="Checking user..." />
);
}
}
return withRouter(withAccess);
};
export default WithAccess;
这正在工作。但是,当我从管理员帐户切换到用户帐户或反之亦然时,WithAccess会传递以前的角色。
为解决问题,我在代码沙箱中重新制作了登录/注册部分:link to code sandbox。
最佳复制方式:
我试图理解为什么从帐户类型切换时WithAccess会传递以前的角色,但还无法弄清。
答案 0 :(得分:1)
您的应用程序中存在内存泄漏!抓住这些技巧很棘手:) https://codesandbox.io/s/k3218vqq8o
在With Access HOC中,您必须取消收听firebase onAuth回调。
如果您在某处添加了侦听器,则始终记住要清理组件!
componentDidMount() {
//Retain a reference to the unlistener callback
this.fireBaseAuthUnlistener = Auth.onAuthStateChanged(async user => {
if (user) {
const idTokenResult = await user.getIdTokenResult(true);
console.log("ID Token claims: ", idTokenResult.claims);
console.log("ROLE : ", role);
if (condition(user) && idTokenResult.claims.role === role) {
this.setState({ isLoading: false });
console.log(user);
} else {
this.props.history.push(`/login`);
}
}
});
}
componentWillUnmount() {
//Unlisten when unmounting!
this.fireBaseAuthUnlistener && this.fireBaseAuthUnlistener();
this.fireBaseAuthUnlistener = undefined;
}
答案 1 :(得分:0)
我认为您应该大大简化路由结构,首先将经过身份验证的路由包装在ProtectedRoutes
HOC中。此路由仅允许经过身份验证(登录)的用户查看。然后添加另一个RequireAdmin
HOC,以检查role
是否为admin
。如果不是,它将重定向到NotFound
页。
经过身份验证的用户可以访问/u/dashboard
,但是当他们单击Admin Dashboard
链接时,将显示一个NotFound
页面。但是,管理员可以同时访问两者!
要测试:
Login
testuser123@example.com
以123456
身份登录Admin Dashboard
NotFound
页Go Back
Dashboard
Logout
Login
。admin@example.com
以123456
身份登录User Dashboard
和Admin Dashboard
(两者均起作用)工作示例:
容器/受保护的路线
import isEmpty from "lodash/isEmpty";
import React, { Component, Fragment } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import Navigation from "../../components/Website/Navigation";
class ProtectedRoutes extends Component {
componentDidMount = () => {
if (isEmpty(this.props.user) || !this.props.user.email) {
this.props.history.push("/notfound");
}
};
componentDidUpdate = prevProps => {
if (this.props.user !== prevProps.user) {
this.props.history.push("/notfound");
}
};
render = () =>
this.props.user.email ? (
<Fragment>
<Navigation />
{this.props.children}
</Fragment>
) : null;
}
export default connect(state => ({ user: state.user }))(
withRouter(ProtectedRoutes)
);
容器/ RequireAdmin
import isEmpty from "lodash/isEmpty";
import { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
class RequireAdmin extends Component {
componentDidMount = () => {
if (isEmpty(this.props.user) || this.props.user.role !== "admin") {
this.props.history.push("/notfound");
}
};
componentDidUpdate = prevProps => {
if (this.props.user !== prevProps.user) {
this.props.history.push("/notfound");
}
};
render = () =>
isEmpty(this.props.user) || this.props.user.role === "admin"
? this.props.children
: null;
}
export default connect(state => ({ user: state.user }))(
withRouter(RequireAdmin)
);
路线(主要)
import React from "react";
import { Route, Switch } from "react-router-dom";
import ProtectedRoutes from "../containers/ProtectedRoutes";
import Admin from "../components/Admin";
import Home from "../pages/Website/Home";
import NotFound from "../pages/Website/NotFound";
import LoginForm from "../containers/LoginForm";
import RegisterForm from "../containers/RegisterForm";
import User from "../components/User";
const Routes = () => (
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={LoginForm} />
<Route exact path="/register" component={RegisterForm} />
<Route exact path="/notfound" component={NotFound} />
<ProtectedRoutes>
<Route path="/u" component={User} />
<Route path="/a" component={Admin} />
</ProtectedRoutes>
</Switch>
</div>
);
export default Routes;
组件/用户(受用户保护的路由)
import React, { Fragment } from "react";
import { Route, Switch } from "react-router-dom";
import Dashboard from "../../pages/User/Dashboard";
import NotFound from "../../pages/Website/NotFound";
const User = ({ match }) => (
<Fragment>
<Switch>
<Route exact path={`${match.url}/dashboard`} component={Dashboard} />
<Route path={`${match.url}`} component={NotFound} />
</Switch>
</Fragment>
);
export default User;
组件/管理员(受管理员保护的路由)
import React, { Fragment } from "react";
import { Route, Switch } from "react-router-dom";
import RequireAdmin from "../../containers/RequireAdmin";
import Dashboard from "../../pages/Admin/Dashboard";
import NotFound from "../../pages/Website/NotFound";
const Admin = ({ match }) => (
<RequireAdmin>
<Switch>
<Route exact path={`${match.url}/dashboard`} component={Dashboard} />
<Route path={`${match.url}`} component={NotFound} />
</Switch>
</RequireAdmin>
);
export default Admin;