reactjs - Keycloak & React Router 路由用于公共/登录 & Authenticated & 基于角色的路由
问题描述
我确实看到了 Tyler McGinnis https://tylermcginnis.com/react-router-protected-routes-authentication/ React Router v4 的受保护路由和身份验证
我使用 react-router-dom v 5.1.2
我在 index.js 中初始化 keycloak 并将其作为道具传递给 App.js 我可以得到 keycloak.idTokenParsed.preferred_username 它们是:SuperAdmin,Admin,Manager,User。我已经有公共路由和经过身份验证的路由,我想弄清楚的部分是如何向其中添加基于角色的路由?这样只有超级管理员可以访问 /create/ 并且管理员可以访问 /edit/。在 Routes.jsx 中,我有 publicRoutes 和 privateRoutes 的 const 我只是为 SuperAdmin、Admin、Manager、User 做 4 还是有更好的方法来做到这一点?
路线.jsx*
import Fcontainer from "../containers/Factories/Fcontainer"
import LContainer from "../containers/Lines/LContainer"
import AddLContainer from "../containers/Lines/AddLContainer"
import AddFContainer from "../containers/Factories/AddFContainer"
import LandingPageContainer from "../containers/common/LandingPageContainer"
export const publicRoutes = [
{ path: "/login", type: "public", name: "landing page", component: LandingPageContainer },
]
export const privateRoutes = [
{ path: "/dir/factories", name: "list of factories", component: Fcontainer },
{ path: "/dir/lines", name: "List of lines", component: LContainer },
{ path: "/dir/lines/create", name: "create line", component: AddLContainer },
{ path: "/dir/factories/create", name: "Create factory", component: AddFContainer },
{ path: "/dir/factories/edit/:factoryId", name: "Edit factory", component: AddFContainer },
{ path: "/dir/lines/edit/:lineId", name: "Edit line", component: AddLContainer }
]
应用程序.js
import React, { useEffect } from "react";
import { Switch, withRouter } from "react-router-dom";
import { publicRoutes, privateRoutes } from "../src/routes/Routes";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { fetchApplicationStyle } from './actions/GetAppStyleAction'
const AppContainer = ({ keycloak }) => {
if(keycloak && keycloak.token) {;
console.log('App.js preferred_username ', keycloak.idTokenParsed.preferred_username);
}
useEffect(() => {
async function fetchStyleApi() {
await fetchApplicationStyle();
if (keycloak && keycloak.authenticated) {
await fetchRoleList()
}
}
fetchStyleApi()
}, [fetchApplicationStyle, fetchRoleList])
return (
<div>
<div>
<ThemeProvider theme={theme}>
{(keycloak && keycloak.token) ?
<React.Fragment>
<Switch>
{privateRoutes.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={'/fcm/factories'} key={'list of factories'} />
</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>
}
</ThemeProvider>
</div>
</div>
);
}
const mapStateToProps = state => {
const { roleAndPrivileges, applicationStyleState } = state;
const theme = ThemeCreator(applicationStyleState.appThemeData)
return {
roleAndPrivileges,
theme
}
}
const mapDispatchToProps = { fetchRoleList, fetchApplicationStyle }
const App = connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(AppContainer))
export default App
解决方案
这可能会对您有所帮助,但它是用另一个库实现的,但您可以限制 React Routes / Component / Functions
安装@react-keycloak/web 库
前置要求:keycloak-js 9.0.2 或更高版本
npm install --save keycloak-js
npm install --save @react-keycloak/web
在项目的文件夹中创建一个keycloak.js
文件,src
内容如下,根据需要设置 Keycloak 实例。
设置 Keycloak 实例
import Keycloak from 'keycloak-js'
const keycloakConfig = {
url: 'http://localhost:8080/auth',
realm: 'Demo',
clientId: 'react-app'
}
const keycloak = new Keycloak(keycloakConfig);
export default keycloak
然后将您的应用程序包装在里面KeycloakProvider
并将keycloak
实例作为道具传递
import React from 'react';
import { KeycloakProvider } from '@react-keycloak/web'
import keycloak from './keycloak'
import AppRouter from './routes'
const App = () => {
return (
<KeycloakProvider keycloak={keycloak}>
<AppRouter/>
</KeycloakProvider>
)
}
正如您在 AppRouter 组件useKeycloak
中看到的那样,没有第一个参数,因为我们不需要使用该 keycloak 实例对象。但是正在使用第二个initialized
参数,因为PrivateRoute
正在使用 keycloak 实例,并且 keycloak 对象需要在初始化时可用PrivateRoute
,这意味着在 keycloak 初始化之前无法返回路由
import { useKeycloak } from '@react-keycloak/web';
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Menu from '../components/Menu';
import HomePage from '../pages/HomePage';
import { PrivateRoute } from '../utilities/PrivateRoute';
import ProtectedPage from '../pages/ProtectedPage';
export const AppRouter = () => {
const [, initialized] = useKeycloak();
if (!initialized) {
return <h3>Loading ... !!!</h3>;
}
return (<>
<Menu />
<BrowserRouter>
<Switch>
<Route exact path="/" component={HomePage} />
<PrivateRoute roles={['RealmAdmin']} path="/protected" component={ProtectedPage} />
</Switch>
</BrowserRouter>
</>
);
};
在项目的文件夹中创建一个PrivateRoute.js
文件,src/utilities
内容如下。
import { useKeycloak } from '@react-keycloak/web';
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
export function PrivateRoute({ component: Component, roles, ...rest }) {
const [keycloak] = useKeycloak();
const isAutherized = (roles) => {
if (keycloak && roles) {
return roles.some(r => {
const realm = keycloak.hasRealmRole(r);
const resource = keycloak.hasResourceRole(r);
return realm || resource;
});
}
return false;
}
return (
<Route
{...rest}
render={props => {
return isAutherized(roles)
? <Component {...props} />
: <Redirect to={{ pathname: '/', }} />
}}
/>
)
}
此外,还有两种使用 Keycloak 限制组件/功能的方法,如下例所示