基于角色的反应路由器

问题描述 投票:0回答:3

根据用户角色在react-router中有条件地渲染路由的最佳方法是什么。我遇到的情况是,并非所有角色都有权查看某些路线。我还需要处理子路线。因此,如果主要路线之一类似于 /posts,我只希望管理员和学生访问该路线及其子路线(例如 posts/today)。我正在使用react-router版本5.3.2。

reactjs react-router role-base-authorization
3个回答
9
投票

我对react-router-dom v6也有类似的问题,但你可以调整它以适应你的好处

首先创建一个事实来源,无论用户是否可以访问某些路由,对于基于角色的身份验证,我会考虑像这样的钩子

export function useUserRoles() {

    // some logic or api call to get the roles
    // for demonstration purposes it's just hard coded
    const userRoles: Array<typeof UserRoles[number]> = ['admin', 'root'];

    // return the current user roles
    return userRoles;
}

然后我们创建一个组件,可以决定使用该钩子是渲染路由还是重定向到登录(或某个页面)

export function RolesAuthRoute({ children, roles }: { children: ReactNode, roles: Array<typeof UserRoles[number]> }) {

    const userRoles = useUserRoles();

    const canAccess = userRoles.some(userRole => roles.includes(userRole));


    if (canAccess)
        return (
            <Fragment>
                {children}
            </Fragment>
        );

    return (<Navigate to="/dashboard/login" />);
}

然后定义的路由包装该组件以充当警卫,决定您是否可以访问该路由

            <Route
                path="users"
                element={
                    <Suspense fallback={<ProjectLayoutLoader />}>

                        <RolesAuthRoute roles={['admin']}>

                            <ProjectUsersPage />

                        </RolesAuthRoute>

                    </Suspense>
                } />


0
投票

我有两个角色“家长”和“经理”,我编写这段代码并为我工作:

function App(props) {

  return (

          <div className="App ">
            <Routes>
                    <Route exact path="/" element={<Home />} />
             
           <Route exact path="/" element={<ProtectedRoute />}>
              <Route exact path="/admin" element={<StudentLists />} />
              <Route exact path="/student_pk_api/:id" element={<IdFilter />} />
              <Route exact path="/absentes" element={<AbsentStu />} />
              <Route exact path="/students_by_field_and_level_:grade_api/:studyfield_code" element={<FieldGradeFilter />} />
              <Route exact path="/absent_yesterday_api" element={<YesterdayAbs />} />
              <Route exact path="/absent_today_api" element={<TodayAbs />} /> 
           </Route>
           <Route exact path="/" element={<ProtectedUserRoute />}>
           <Route exact path="/users" element={<SchoolDit />} />
           </Route>
             </Routes>
          </div>
    

  );
}

为了检查角色,我编写了两个函数:

对于经理角色:

const  ProtectedRoute = () => {
    const [manager, setmaneger] = useState(localStorage.getItem("role")==="manager"?true:null);

    return manager ? <Outlet /> : <Navigate to="/" />;
}

  export default ProtectedRoute

对于家长角色:

const  ProtectedUserRoute = () => {
   
    const [parent, setParent] = useState(localStorage.getItem("role")==="parent"?true:null);

    return parent ? <Outlet /> : <Navigate to="/" />;
}
export default ProtectedUserRoute


0
投票

当然,有很多方法可以实现这一点。我将与 React Router DOM v6 分享我的工作示例。

步骤:

  1. 使用钩子获取身份验证数据(可以使用 context 或 Redux)。
  2. 使用角色数据为受保护路径创建映射对象
  3. 使用与每个路径角色相关的唯一 ID 定义路由。
  4. 组合 ProtectedRoute 中的所有内容并使用
    useOutlet
    检索路径数据。

那么让我们开始吧。

1。 useAuth 钩子

const token = await getToken() 

const useAuth = () => {
  const [user, setUser] = useState<user | null>(null);

  useEffect(() => {
    if (token) {
      const isTokenValid = checkTokenValidity(token);
      const userData = isTokenValid ? decodeToken(token) : null;

      if (isTokenValid) {
        setUser(userData);
      } else {
        setUser(null);

        removeCookie("token");
      }
    } else {
      setUser(null);
    }
  }, []);

  return { user };
};

** 我认为内部方法是直接的,不需要扩展

现在让我们定义路由器并创建角色映射对象。

2。定义路径角色映射对象

export const paths = {
  boo: { label: "Boo", path: "boo", roles: null },
  foo: { label: "Foo", path: "foo", roles: ["admin"] },
  zoo: { label: "API", path: "api", roles: ["admin", "manager"] },
};
  • 我喜欢这种方法,因为我也可以将此映射用于导航栏,但您可以决定要包含哪些属性。

3.定义路由器并使用路径映射对象

const createRouteId = (key: keyof typeof paths) => {
  return JSON.stringify({ [key]: paths[key].roles });
};

export const router = createBrowserRouter([
  {
    path: "/",
    element: <ProtectedRoute />,
    children: [
      { path: paths.boo.path, element: <Boo/>, id: createRouteId("boo") },
      { path: paths.foo.path, element: <Foo />, id: createRouteId("foo") },
      { path: paths.zoo.path, element: <Zoo/>, id: createRouteId("zoo") }
    ],
    errorElement: <ErrorPage />,
  }]

id
属性应该是唯一的,并使用方法
createRouteId

包含每个路径的角色数据
  1. 将所有内容合并到 ProtectdRoute 组件中并利用 useOutlet
const isRoleValid = (userRoles: string[] | undefined, outletRole: string[] | null) => {
  if (!userRoles) return false;
  if (!outletRole) return true;
  return userRoles.some((role) => outletRole.includes(role));
};

function ProtectdRoute() {
  const { user } = useAuth();
  const location = useLocation();
  const getRouteIdFromOutlet = useOutlet();

  useEffect(() => {
    const routeLocationData = getRouteIdFromOutlet?.props.children.props.match.route;
    const locationKey = routeLocationData?.path;
    const locationRolesObj = routeLocationData?.id && JSON.parse(routeLocationData?.id);
    if (locationRolesObj && locationKey) {
      const locationRoles = locationRolesObj[locationKey];
      if (!isRoleValid(user?.roles, locationRoles)) {
        throw new Response("unauthorized", { status: 403 });
      }
    }
  }, [user, location.pathname]);

  return user ? <Outlet /> : <Login />
}

useOutlt 非常嵌套,但它确实包含 id 信息,其中包括每个特定路径的角色。

© www.soinside.com 2019 - 2024. All rights reserved.