reactjs - 用于公共(public)/登录、经过身份验证和基于角色的路由的 Keycloak 和 React Router 路由

标签 reactjs authentication keycloak react-router-dom role-based-access-control

我确实看到泰勒·麦金尼斯 https://tylermcginnis.com/react-router-protected-routes-authentication/使用 React Router v4 保护路由和身份验证

我使用react-router-dom v 5.1.2

我在index.js中初始化keycloak并将其作为 Prop 传递给App.js我可以得到keycloak.idTokenParsed.preferred_username它们是: super 管理员, 行政, 经理, 用户。 我已经有公共(public)路由和经过身份验证的路由,我想弄清楚的部分是如何向其添加基于角色的路由?这样只有 super 管理员才能访问/create/,管理员才能访问/edit/。 在 Routes.jsx 中,我为 publicRoutes 和 privateRoutes 设置了 const,我是否只需为 SuperAdmin、Admin、Manager、User 设置 4 还是有更好的方法?

Routes.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 }
]

App.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路由/组件/函数

安装@react-keycloak/web库

预置要求:keycloak-js 9.0.2 或更高版本

npm install --save keycloak-js
npm install --save @react-keycloak/web

在项目的 src 文件夹中创建一个 keycloak.js 文件,其中包含以下内容,以根据需要设置 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 实例作为 prop 传递

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>
    </>
    );
};

在项目的 src/utilities 文件夹中创建一个 PrivateRoute.js 文件,其中包含以下内容。

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 限制组件/功能的方法,如下例所示

https://medium.com/@cagline/authenticate-and-authorize-react-routes-component-with-keycloak-666e85662636

关于reactjs - 用于公共(public)/登录、经过身份验证和基于角色的路由的 Keycloak 和 React Router 路由,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61070028/

相关文章:

javascript - 无法理解react组件中带有@符号的函数

azure - AAD B2C :Unable to Authenticate web api after changing Authority URL from login. microsoftonline.com 到 xyz.b2clogin.com

java - 带 Keycloak 的 Quarkus GraphQL 客户端

spring-boot - 无法从 KeyCloak 获取访问 token

javascript - 无法点击 iOS 模拟器中的文本字段或按钮

javascript - 包装 JavaScript 函数引用

javascript - 使用 React 评级组件,如何根据评级显示自定义消息?

wordpress - 无需身份验证即可使用 WP-REST 获取帖子

android - 检查登录Android的用户详细信息

oauth - Swagger 中的 Keycloak 集成