[英]How can I show a different sidebar based on user information? After logged in. ReactJS
I have a react js application which authenticates with azure active directory and then it shows this menu: 我有一个react js应用程序,它使用azure活动目录进行身份验证,然后显示以下菜单:
However I want that after logged in, based on information received from Azure AD Like Roles Or groups, then a different side bar with different options is shown, so I can have the same APP with different groups of users and the menu will depend on the role or group. 但是我希望在登录后,根据从Azure AD Like Roles Or groups收到的信息,然后显示具有不同选项的不同侧栏,因此我可以使用不同用户组的相同APP,菜单将取决于角色或团体。
This is my app structure: 这是我的app结构:
And here is the relevant files: 以下是相关文件:
index.js 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();
Sidebebar.js Sidebebar.js
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);
dashapp.js dashapp.js
import React from "react";
import { Provider } from "react-redux";
import { store, history } from "./redux/store";
import PublicRoutes from "./router";
import { ThemeProvider } from "styled-components";
import { LocaleProvider } from "antd";
import { IntlProvider } from "react-intl";
import themes from "./settings/themes";
import AppLocale from "./languageProvider";
import config, {
getCurrentLanguage
} from "./containers/LanguageSwitcher/config";
import { themeConfig } from "./settings";
import DashAppHolder from "./dashAppStyle";
import Boot from "./redux/boot";
const currentAppLocale =
AppLocale[getCurrentLanguage(config.defaultLanguage || "english").locale];
const DashApp = () => (
<LocaleProvider locale={currentAppLocale.antd}>
<IntlProvider
locale={currentAppLocale.locale}
messages={currentAppLocale.messages}
>
<ThemeProvider theme={themes[themeConfig.theme]}>
<DashAppHolder>
<Provider store={store}>
<PublicRoutes history={history} />
</Provider>
</DashAppHolder>
</ThemeProvider>
</IntlProvider>
</LocaleProvider>
);
Boot()
.then(() => DashApp())
.catch(error => console.error(error));
export default DashApp;
export { AppLocale };
Question: 题:
How do I modify this code to render a different sidebar depending on the logged in user? 如何根据登录用户修改此代码以呈现不同的侧边栏?
The first step highly relies of how your authentication is configured. 第一步高度依赖于您的身份验证配置方式。 Once you logged in with azure you will want to store the retrieved user profile inside your redux data storage.
使用azure登录后,您将希望将检索到的用户配置文件存储在redux数据存储中。
If you are using react redux with adal, you should have a login action dispatched. 如果您将反应redux与adal一起使用,则应该调度登录操作。 On success of this action, inside your login reducer, pick what you need from the information retrieved.
在此操作成功后,在您的登录reducer中,从检索到的信息中选择您需要的内容。
The following code is only a sample of what you could be left with after implementing such a reducer. 以下代码仅是实现此类reducer后可能留下的样本。 AD will certainly give you a group ID instead of the 'admin' role shown in this example, simply modify the check accordingly.
AD肯定会为您提供一个组ID而不是此示例中显示的“admin”角色,只需相应地修改检查。 You may have to set
groupMembershipClaims
to "SecurityGroup"
or "All"
in your app's manifest in AAD in order to add this piece of information inside the authentication response. 您可能必须在AAD的应用程序清单中将
groupMembershipClaims
设置为"SecurityGroup"
或"All"
,以便在身份验证响应中添加此信息。
case Action.LOGIN_SUCCESS:
return {
...state,
username: action.username,
isAdmin: action.role === 'admin'
}
You may need to customize what informations AD sends you from the AD dashboard. 您可能需要自定义AD从AD仪表板发送的信息。
What's left is trivial : 剩下的是微不足道的:
Connect the store to the components where user permissions are needed 将商店连接到需要用户权限的组件
connect(state => ({ user: state.loginReducer, }))
Customize the render conditionally inside your component 在组件内有条件地自定义渲染
render() { const { user } = this.props; return ( <div className={classNames.navbar}> { user.isAdmin && <Link to="adminpanel" label="Admin Panel" /> } <Link to="about" label="About" /> </div> ) }
By the way, you should set the locale inside the redux storage aswell if you need to handle dynamic language switching. 顺便说一下,如果需要处理动态语言切换,还应该在redux存储中设置语言环境。
In the presented code I did not found a way to figure out if user is authenticated or not. 在提供的代码中,我没有找到一种方法来确定用户是否经过身份验证。 I think that's the main point here.
我认为这是重点。
The easiest way to do that, is to check for a cookie. 最简单的方法是检查cookie。 That of course depends on how your authentication is working.
这当然取决于您的身份验证的工作方式。 You need to have a function that will check for auth cookie.
您需要有一个检查auth cookie的函数。 Something like this.
像这样的东西。 I'm using
universal-cookie
packages, seems like your app is isomorphic, so you need to pass ssrCookie
on server, you will get that from res.headers.cookies
. 我正在使用
universal-cookie
包,看起来你的应用程序是同构的,所以你需要在服务器上传递ssrCookie
,你将从res.headers.cookies
获得。 If the app is client-side only you can drop ssrCookie
: 如果应用程序只是客户端,则可以删除
ssrCookie
:
import Cookies from 'universal-cookie'
const authCookieName = 'awesome_app__token'
function getCookies(isServer, ssrCookie) {
if (isServer) {
return new Cookies(ssrCookie || '')
} else {
return new Cookies()
}
}
export function isLoggedIn(isServer, ssrCookie) {
return !!getToken(getCookies(isServer, ssrCookie), ssrCookie)
}
function getToken(isServer, ssrCookie) {
return getCookies(isServer, ssrCookie).get(authCookieName)
}
So, now, in you react code you check if user is logged in: 那么,现在,在您对代码做出反应时,您会检查用户是否已登录:
import { isLoggedIn } from './thePathToFileWhereThisFunctionsWasSet'
class Sidebar extends Component {
render() {
return (
{isLoggedIn() ? (<div>For logged in user</div>) : (<div>For not logged in users</div>)}
)
}
}
After digging a bit in react-adal
code, I saw that the logged
state is not passed through the children's context. 在使用
react-adal
代码挖掘一下之后,我发现logged
状态没有通过子代的上下文传递。 so as far as I see, the only way to get the current user is : 所以,据我所知,获得当前用户的唯一方法是:
import { authContext } from './adalConfig';
authContext.getCachedUser();
This only after you have wrapped your main component with runWithAdal
as you did. 这只是在你使用
runWithAdal
包装主组件之后。
With that you may create a new component SidebarOptions
which will decide upon the prop user
( user = authContext.getCachedUser();
) which options to render. 有了它,你可以创建一个新的组件
SidebarOptions
,它将决定prop user
( user = authContext.getCachedUser();
)要呈现哪些选项。
Another option is to create a PR to react-adal
and make sure to pass these values through the wrapped component or all the children s components (using
react-context`) 另一个选择是创建一个PR
react-adal
并确保通过包装组件或所有子s components (using
传递这些值s components (using
react-context`)
Also, as I can see you are using redux, so a it's better to save the user info with dispatching an action upon first rendered component with the authContext.getCachedUser()
data and set it on your store for easy access across the app. 此外,我可以看到您正在使用redux,因此最好通过使用
authContext.getCachedUser()
数据在第一个呈现的组件上调度操作来保存用户信息,并将其设置在您的商店中以便在整个应用程序中轻松访问。 a good place to do this is in the success callback of withAdalLoginApi
. 这样做的好地方是
withAdalLoginApi
的成功回调。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.