问题
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