【问题标题】:Keycloak integration with react-adminKeycloak 与 react-admin 的集成
【发布时间】:2021-07-26 15:02:57
【问题描述】:

搜索一些关于如何将 keycloak 与 react admin 应用程序集成以进行身份​​验证的示例。

【问题讨论】:

    标签: reactjs react-redux react-router react-hooks react-admin


    【解决方案1】:

    App.js 示例:

    // your other imports
    import { ReactKeycloakProvider } from "@react-keycloak/web";
    import Keycloak from "keycloak-js";
    import Cookies from "js-cookie";
    
    // we used cookies that backend is writing, because we had front as a production static build 
    const initOptions = {
      url: Cookies.get("REACT_APP_KEYCLOAK_URL"),
      realm: Cookies.get("REACT_APP_KEYCLOAK_REALM"),
      clientId: Cookies.get("REACT_APP_KEYCLOAK_CLIENT_ID"),
      onLoad: "login-required",
    };
    
    const keycloak = Keycloak(initOptions);
    
    const onToken = () => {
      if (keycloak.token && keycloak.refreshToken) {
        localStorage.setItem("token", keycloak.token);
        localStorage.setItem("refresh-token", keycloak.refreshToken);
      }
    };
    
    const onTokenExpired = () => {
      keycloak
        .updateToken(30)
        .then(() => {
          console.log("successfully get a new token", keycloak.token);
        })
        .catch(() => {
          console.error("failed to refresh token");
        });
    };
    
    // for data provider, it writes token to an authorization header
    const fetchJson = (url, options = {}) => {
      if (!options.headers) {
        options.headers = new Headers({ Accept: "application/json" });
      }
      if (keycloak.token) {
        options.headers.set("Authorization", "Bearer " + keycloak.token);
      } else if (localStorage.getItem("token")) {
        options.headers.set(
          "Authorization",
          "Bearer " + localStorage.getItem("token")
        );
      }
      return fetchUtils.fetchJson(url, options);
    };
    
    const customDataProvider = dataProvider("/api/v1", fetchJson);
    
    const theme = createMuiTheme({
      ...defaultTheme,
      sidebar: {
        width: 110,
        closedWidth: 40,
      },
    });
    
    const fetchResources = (permissions) => {
      let knownResources = [];
      if (permissions) {
        const resource = (
          <Resource
            name="feeds"
            list={FeedList}
            create={FeedCreate}
            edit={StateEdit}
            icon={CollectionsBookmark}
          />
        );
        knownResources.push(resource);
      } else {
        const resource = (
          <Resource name="feeds" list={FeedList} icon={CollectionsBookmark} />
        );
        knownResources.push(resource);
      }
      return knownResources;
    };
    
    const CustomAdminWithKeycloak = () => {
      const customAuthProvider = useAuthProvider(
        Cookies.get("REACT_APP_KEYCLOAK_CLIENT_ID")
      );
      return (
        <Admin
          theme={theme}
          dataProvider={customDataProvider}
          authProvider={customAuthProvider}
          loginPage={false}
          title="Inventory Splitter"
          layout={CustomLayout}
        >
          {fetchResources}
        </Admin>
      );
    };
    
    const CustomAdmin = () => {
      return (
        <Admin
          theme={theme}
          dataProvider={customDataProvider}
          loginPage={false}
          title="Inventory Splitter"
          layout={CustomLayout}
        >
          <Resource
            name="feeds"
            list={FeedList}
            create={FeedCreate}
            edit={StateEdit}
            icon={CollectionsBookmark}
          />
        </Admin>
      );
    };
    
    // we have a feature to completely switch off the authorization process through env variable on backend
    const App = () => {
      const useKeycloak = Cookies.get("USE_KEYCLOAK") === "true";
      return useKeycloak ? (
        <ReactKeycloakProvider
          authClient={keycloak}
          LoadingComponent={<div></div>}
          initOptions={initOptions}
          onTokens={onToken}
          onTokenExpired={onTokenExpired}
        >
          <React.Fragment>
            <CustomAdminWithKeycloak />
            <ThemeProvider theme={theme}>
              <Footer />
            </ThemeProvider>
          </React.Fragment>
        </ReactKeycloakProvider>
      ) : (
        <React.Fragment>
          <CustomAdmin />
          <ThemeProvider theme={theme}>
            <Footer />
          </ThemeProvider>
        </React.Fragment>
      );
    };
    
    export default App;
    

    authProvider.js

    import { useKeycloak } from '@react-keycloak/web'
    import jwt_decode from 'jwt-decode'
    
    const useAuthProvider = (clientID:string) => {
        const { keycloak } = useKeycloak();
        return ({
            login: () => keycloak.login(),
            checkError: () => Promise.resolve(),
            checkAuth: () => {
                return keycloak.authenticated &&
                keycloak.token ? Promise.resolve() : Promise.reject("Failed to obtain access token.");
            },
            logout: () => keycloak.logout(),
            getIdentity: () => {
                if (keycloak.token) {
                    const decoded : any = jwt_decode(keycloak.token);
                    const id = decoded.sub
                    const fullName = decoded.name
                    return Promise.resolve({id, fullName});
                }
                return Promise.reject("Failed to get identity");
            },
            getPermissions:() => {
                let hasRole = false;
                if (keycloak.token) {
                    const decoded : any = jwt_decode(keycloak.token);
                    decoded.resource_access[clientID].roles.forEach((el: string) => {
                        if (el === "admin") {
                            hasRole = true;
                            return
                        }
                    });
                }
                if (hasRole) {
                    return Promise.resolve(true);
                }
                return Promise.resolve(false);
            },
        });
    };
    
    export default useAuthProvider;
    

    如果你想根据权限隐藏一些组件,像这样使用smth:

    import * as React from 'react';
    import {
        useListContext,
        usePermissions,
        TopToolbar,
        CreateButton,
    } from 'react-admin';
    import PublishIcon from '@material-ui/icons/Publish';
    
    const CustomListActions = (props) => {
        const permissions = usePermissions();
        const { basePath } = useListContext();
        return (
            <TopToolbar>
                {permissions.permissions && <CreateButton label='Upload' basePath={basePath} icon={<PublishIcon/>}/>}
            </TopToolbar>
        );
    };
    
    export default CustomListActions;
    

    【讨论】:

    • 嗨@mar​​ia-efimenko,你有没有一个带有你的例子的git repo作为演示?
    • @justinb,现在很遗憾,因为它是 gitlab 上的私有存储库,我不能分享它。但是,这些示例足以让您入门)
    猜你喜欢
    • 2022-07-07
    • 2019-04-28
    • 2021-12-14
    • 2021-01-10
    • 2020-02-20
    • 1970-01-01
    • 2020-05-28
    • 2021-12-02
    • 2023-01-30
    相关资源
    最近更新 更多