问题描述
搜索一些关于如何将 keycloak 与 react admin 应用程序集成以进行身份验证的示例。
解决方法
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;