React Router v5.1.2 Public & Protected Authenticated & Role Based routes

放肆的年华 提交于 2021-01-29 16:25:36

问题


Goal is to have /login as the only public route, once logged in user has routes based on user role. Authentication is done with Keycloak I get users from keycloak.idTokenParsed.preferred_username: admin, manager, engineer, operator. If operator tries to go to role restricted route gets redirected to /notauthorized page. (This part not done) If not logged in user gets redirected to /login page. (This part is done/works)

Is there a better way to do this? Not repeating routes & adding additional users in Routes.jsx kind of a mess. How do I implement role restricted redirect to /notauthorized?

App.js (does not have all the imports and missing bottom part with mapStateToProps, mapDispatchToProps & export default App )

import React, { useEffect } from "react";
import { Route, Redirect, Switch } from "react-router-dom"

let routeWithRole = [];
let user = '';

const AppContainer = ({ keycloak }) => {
  if(keycloak && keycloak.token) {
    user = keycloak.idTokenParsed.preferred_username
    if( user === 'admin') {
      routeWithRole = admin;
    } else if( user === 'engineer') {
      routeWithRole = engineer
    } else if(user === 'manager') {
      routeWithRole = manager
    } else {
      routeWithRole = operator
    }
  }

   return (
    <div>
          {(keycloak && keycloak.token) ?
            <React.Fragment>
                <Switch>

                  {routeWithRole.map((prop, key) => {
                    console.log('App.js Prop & Key ', prop, key)
                    return (
                      <Route
                        path={prop.path}
                        key={key}
                        exact={true}
                        component={prop.component}
                      />
                    );
                  })}
                  <Redirect from={'/'} to={'/dashboard'} key={'Dashboard'} />
                </Switch>
            </React.Fragment>
            :
            <React.Fragment>
              <Switch>
                {publicRoutes.map((prop, key) => {
                  return (
                    <Route
                      path={prop.path}
                      key={key}
                      exact={true}
                      component={(props) =>
                        <prop.component
                          keycloak={keycloak}
                          key={key} {...props} />
                      }
                    />
                  );
                })}
                <Redirect from={'/'} to={'/login'} key={'login'} />
              </Switch>
            </React.Fragment>
          }
      </div>
  )
}

Routes.jsx (missing all the impotrs)

export const publicRoutes = [
  { path: "/login", type: "public", name: "landing page", component: LandingPageContainer },
]

export const admin = [
  { path: "/createUser", name: "Create User", component: CreateUser},
  { path: "/editUser", name: "Edit User", component: EditUser},
  { path: "/createdashboard", name: "Create Dashboard", component: CreateDashboard },
  { path: "/editashboard", name: "Edit Dashboard", component: EditDashboard },
  { path: "/createcalendar", name: "Create Calendar", component: CreateCalendar },
  { path: "/editcalendar", name: "list of factories", component: EditCalendar },
  { path: "/dashboard", name: "Dashboard", component: Dashboard }
]

export const engineer = [
  { path: "/createdashboard", name: "Create Dashboard", component: CreateDashboard },
  { path: "/editashboard", name: "Edit Dashboard", component: EditDashboard },
  { path: "/dashboard", name: "Dashboard", component: Dashboard },
  { path: "/notauthorized", name: "Not Authorized", component: Notauthorized }
]

export const manager = [
  { path: "/createcalendar", name: "Create Calendar", component: CreateCalendar },
  { path: "/editcalendar", name: "Edit Calendar", component: EditCalendar },
  { path: "/dashboard", name: "Dashboard", component: Dashboard },
  { path: "/notauthorized", name: "Not Authorized", component: Notauthorized }
]

export const operator = [
  { path: "/dashboard", name: "Dashboard", component: Dashboard },
  { path: "/notauthorized", name: "Not Authorized", component: Notauthorized }
]

回答1:


I will consider the option when we have known "keycloak" before react initialization (not async loading data for "keycloak"). You will be able to improve if you understand the idea

The main idea is to show all routes but almost all of them will be protected routes. See the example:

render (
  <Switch>
    <Route exact path="/login"> // public route
      <LandingPageContainer />
    </Route>
    <AuthRoute exact path="/dashboard"> // for any authorized user
      <Dashboard />
    </AuthRoute>
    <AdminRoute path="/create-user"> // only for admin route
      <CreateUser />
    </AdminRoute>
    <AdminOrEngineerRoute path="/create-dashboard"> // only for admin or engineer route
      <CreateDashboard />
    </AdminOrEngineerRoute>
    <Redirect to="/dashboard" /> // if not matched any route go to dashboard and if user not authorized dashboard will redirect to login
  </Switch>
);

Then you can create list of components like this:

const AVAILABLED_ROLES = ['admin', 'engineer'];

const AdminOrEngineerRoute = ({ children, ...rest }) {
  const role = keycloak && keycloak.token ? keycloak.idTokenParsed.preferred_username : '';

  return (
    <Route
      {...rest}
      render={({ location }) =>
        AVAILABLED_ROLES.includes(role) && ? (
          children
        ) : (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: location }
            }}
          />
        )
      }
    />
  );
}

As a result AdminOrEngineerRoute will allow to pass to this route only admin or engineer in other case you will get /login page

Always yours "IT's Bruise"



来源:https://stackoverflow.com/questions/61131446/react-router-v5-1-2-public-protected-authenticated-role-based-routes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!