javascript - 如何使用 ADAL JS 获取当前用户角色
问题描述
我有以下反应应用程序,它是来自 envato 的模板。我已经使用这个组件集成了 Azure AD 身份验证,它工作得非常好:
https://github.com/salvoravida/react-adal
但是我想创建角色,并且我希望能够根据当前用户拥有的角色在侧边栏上显示菜单项。
我已经知道如何使用应用程序清单在 Azure AD 中创建角色,所以这个问题更多的是关于如何在用户通过身份验证后获取这些角色以及如何根据声明值呈现菜单项。
这里的相关代码:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import DashApp from './dashApp';
import registerServiceWorker from './registerServiceWorker';
import 'antd/dist/antd.css';
import { runWithAdal } from 'react-adal';
import { authContext } from './adalConfig';
const DO_NOT_LOGIN = false;
runWithAdal(authContext, () => {
ReactDOM.render(<DashApp />, document.getElementById('root'));
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./dashApp.js', () => {
const NextApp = require('./dashApp').default;
ReactDOM.render(<NextApp />, document.getElementById('root'));
});
}
},DO_NOT_LOGIN);
registerServiceWorker();
AdalConfig.js
从'react-adal'导入{AuthenticationContext,adalFetch,withAdalLogin};
export const adalConfig = {
tenant: 'abc-af96-4f7c-82db-b6f0bd7ae9b6',
clientId: 'abc-969c-49b2-8a58-78eece990daf',
endpoints: {
api:'abc-083c-4c10-b40f-f1d764319b21'
'apiUrl': 'https://abc.azurewebsites.net/api',
cacheLocation: 'localStorage'
};
export const authContext = new AuthenticationContext(adalConfig);
export const adalApiFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.api, fetch, adalConfig.apiUrl+url, options);
export const withAdalLoginApi = withAdalLogin(authContext, adalConfig.endpoints.api);
仪表板.js
import React, { Component } from 'react';
import LayoutContentWrapper from '../components/utility/layoutWrapper';
import LayoutContent from '../components/utility/layoutContent';
export default class extends Component {
render() {
return (
<LayoutContentWrapper style={{ height: '100vh' }}>
<LayoutContent>
<h1>ISOMORPHIC DASHBOARD HOME</h1>
</LayoutContent>
</LayoutContentWrapper>
);
}
}
路由器.js
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import { connect } from 'react-redux';
import App from './containers/App/App';
import asyncComponent from './helpers/AsyncFunc';
const RestrictedRoute = ({ component: Component, isLoggedIn, ...rest }) => (
<Route
{...rest}
render={props => isLoggedIn
? <Component {...props} />
: <Redirect
to={{
pathname: '/signin',
state: { from: props.location },
}}
/>}
/>
);
const PublicRoutes = ({ history, isLoggedIn }) => {
return (
<ConnectedRouter history={history}>
<div>
<Route
exact
path={'/'}
render={() => <Redirect to="/dashboard" />}
/>
<Route
exact
path={'/signin'}
component={asyncComponent(() => import('./containers/Page/signin'))}
/>
<RestrictedRoute
path="/dashboard"
component={App}
isLoggedIn={isLoggedIn}
/>
</div>
</ConnectedRouter>
);
};
export default connect(state => ({
isLoggedIn: state.Auth.get('idToken') !== null,
}))(PublicRoutes);
侧边栏
import React, { Component } from "react";
import { connect } from "react-redux";
import clone from "clone";
import { Link } from "react-router-dom";
import { Layout } from "antd";
import options from "./options";
import Scrollbars from "../../components/utility/customScrollBar.js";
import Menu from "../../components/uielements/menu";
import IntlMessages from "../../components/utility/intlMessages";
import SidebarWrapper from "./sidebar.style";
import appActions from "../../redux/app/actions";
import Logo from "../../components/utility/logo";
import themes from "../../settings/themes";
import { themeConfig } from "../../settings";
const SubMenu = Menu.SubMenu;
const { Sider } = Layout;
const {
toggleOpenDrawer,
changeOpenKeys,
changeCurrent,
toggleCollapsed
} = appActions;
const stripTrailingSlash = str => {
if (str.substr(-1) === "/") {
return str.substr(0, str.length - 1);
}
return str;
};
class Sidebar extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.onOpenChange = this.onOpenChange.bind(this);
}
handleClick(e) {
this.props.changeCurrent([e.key]);
if (this.props.app.view === "MobileView") {
setTimeout(() => {
this.props.toggleCollapsed();
this.props.toggleOpenDrawer();
}, 100);
}
}
onOpenChange(newOpenKeys) {
const { app, changeOpenKeys } = this.props;
const latestOpenKey = newOpenKeys.find(
key => !(app.openKeys.indexOf(key) > -1)
);
const latestCloseKey = app.openKeys.find(
key => !(newOpenKeys.indexOf(key) > -1)
);
let nextOpenKeys = [];
if (latestOpenKey) {
nextOpenKeys = this.getAncestorKeys(latestOpenKey).concat(latestOpenKey);
}
if (latestCloseKey) {
nextOpenKeys = this.getAncestorKeys(latestCloseKey);
}
changeOpenKeys(nextOpenKeys);
}
getAncestorKeys = key => {
const map = {
sub3: ["sub2"]
};
return map[key] || [];
};
getMenuItem = ({ singleOption, submenuStyle, submenuColor }) => {
const { key, label, leftIcon, children } = singleOption;
const url = stripTrailingSlash(this.props.url);
if (children) {
return (
<SubMenu
key={key}
title={
<span className="isoMenuHolder" style={submenuColor}>
<i className={leftIcon} />
<span className="nav-text">
<IntlMessages id={label} />
</span>
</span>
}
>
{children.map(child => {
const linkTo = child.withoutDashboard
? `/${child.key}`
: `${url}/${child.key}`;
return (
<Menu.Item style={submenuStyle} key={child.key}>
<Link style={submenuColor} to={linkTo}>
<IntlMessages id={child.label} />
</Link>
</Menu.Item>
);
})}
</SubMenu>
);
}
return (
<Menu.Item key={key}>
<Link to={`${url}/${key}`}>
<span className="isoMenuHolder" style={submenuColor}>
<i className={leftIcon} />
<span className="nav-text">
<IntlMessages id={label} />
</span>
</span>
</Link>
</Menu.Item>
);
};
render() {
const { app, toggleOpenDrawer, height } = this.props;
const collapsed = clone(app.collapsed) && !clone(app.openDrawer);
const { openDrawer } = app;
const mode = collapsed === true ? "vertical" : "inline";
const onMouseEnter = event => {
if (openDrawer === false) {
toggleOpenDrawer();
}
return;
};
const onMouseLeave = () => {
if (openDrawer === true) {
toggleOpenDrawer();
}
return;
};
const customizedTheme = themes[themeConfig.theme];
const styling = {
backgroundColor: customizedTheme.backgroundColor
};
const submenuStyle = {
backgroundColor: "rgba(0,0,0,0.3)",
color: customizedTheme.textColor
};
const submenuColor = {
color: customizedTheme.textColor
};
return (
<SidebarWrapper>
<Sider
trigger={null}
collapsible={true}
collapsed={collapsed}
width="240"
className="isomorphicSidebar"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
style={styling}
>
<Logo collapsed={collapsed} />
<Scrollbars style={{ height: height - 70 }}>
<Menu
onClick={this.handleClick}
theme="dark"
className="isoDashboardMenu"
mode={mode}
openKeys={collapsed ? [] : app.openKeys}
selectedKeys={app.current}
onOpenChange={this.onOpenChange}
>
{options.map(singleOption =>
this.getMenuItem({ submenuStyle, submenuColor, singleOption })
)}
</Menu>
</Scrollbars>
</Sider>
</SidebarWrapper>
);
}
}
export default connect(
state => ({
app: state.App.toJS(),
height: state.App.toJS().height
}),
{ toggleOpenDrawer, changeOpenKeys, changeCurrent, toggleCollapsed }
)(Sidebar);
是的,我知道代码太长,但我认为为问题提供一个好的上下文是有意义的。
来自 salvarovida 的 react-adal 打包器在后台使用 adal js 库,因此基本上它是一个包装器。
Aparently 可以使用这行代码获得角色,但不确定如何使用它以及在哪里使用它。
https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/713
解决方案
由于您已经提到您将创建 Azure AD 角色,因此您也可以将用户/组添加到这些角色。
将这些角色映射到用户后,您可以查询用户的 Graph API。
GET https://graph.microsoft.com/v1.0/me
这将返回响应为
HTTP/1.1 200 OK
Content-type: application/json
Content-length: 491
{
"displayName": "displayName-value",
"givenName": "givenName-value",
"mail": "mail-value",
"surname": "surname-value",
"userPrincipalName": "userPrincipalName-value",
"id": "id-value"
}
现在您可以查询 Graph API 以获取与该用户关联的角色
POST /groups/{id}/getMemberGroups
响应将包含映射到用户的所有组的列表。您可以存储您在 web.config 中创建的新创建的组/角色,并检查它们是否是返回列表的一部分。
注意:您需要 Directory.Read.All、Directory.ReadWrite.All、Directory.AccessAsUser.All 的权限才能进行此调用。
来源:https ://docs.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0
您可以使用 Web API 或 JS 进行这些 API 调用。我更喜欢坚持使用 WebAPI 来获取角色并在前端的 API 调用中返回这些角色(以获取菜单、设置权限等)。
此外,请参阅https://graphexplorer.azurewebsites.net/以在代码中实现它们之前测试这些调用。
如果您需要示例应用程序,请参阅此 -使用 Azure AD 应用程序角色和角色声明在 Web 应用程序中进行授权
推荐阅读
- python - 如何在python中以这种格式“Sun,2019年9月22日09:18:13 GMT”获取当前GMT时间
- c - 如何使用 malloc 从函数返回 char 数组
- google-cloud-platform - 从 Node.js 云函数创建 Kubernetes 作业
- c++ - 在 C++ 中添加日期,使用类
- javascript - 如何逐行打印Vue对象
- python - 基于颜色比较找到两个区域之间的匹配
- selenium - Selenium & Scrapy:最后一个 URL 覆盖其他 URL
- wordpress - 为什么我需要在 docker-compose.yml 中显式声明 env_file?
- typescript - 如何在 Typescript 中扩展模块
- security - 高峰期网站的性能很差