简体   繁体   English

如何根据用户信息显示不同的侧边栏? 登录后.ReactJS

[英]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 useruser = 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.

相关问题 登录后根据用户角色显示不同的侧边栏项目或隐藏某些组件(VUEJS) - Show different sidebar items or hide some components based on user-role after login (VUEJS) 用户登录后如何显示弹出消息? - How can I show a pop-up message after an user is logged in? 如何在用户注销时隐藏侧边栏并在用户登录时显示侧边栏? - How to hide sidebar upon user logout and show sidebar when user is logged in? 如何仅在用户登录时运行 ajax。(Django) - How to run ajax only when user is logged in. (Django) 用户登录后如何显示计时器? - How to show timer after user logged? 根据用户是否登录来显示不同导航栏的方法是什么? - What are the ways to show different navbar based on if the user logged or not? 当用户未登录时显示导航栏。希望导航栏在用户拥有令牌后呈现 - NavBar displaying when user is not logged in. Want navbar to render after user has token 如何在HTML页面上创建按钮显示不同的信息 - How can i make buttons on a HTML page show different information 如何根据用户呈现侧边栏 - How can I render sidebar depend on user 在 Shopify 中,我如何获取管理部分的登录用户信息? - In Shopify, how do i get the logged in user information of admin section?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM