簡體   English   中英

如何根據用戶信息顯示不同的側邊欄? 登錄后.ReactJS

[英]How can I show a different sidebar based on user information? After logged in. ReactJS

我有一個react js應用程序,它使用azure活動目錄進行身份驗證,然后顯示以下菜單:

在此輸入圖像描述

但是我希望在登錄后,根據從Azure AD Like Roles Or groups收到的信息,然后顯示具有不同選項的不同側欄,因此我可以使用不同用戶組的相同APP,菜單將取決於角色或團體。

這是我的app結構:

在此輸入圖像描述

以下是相關文件:

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

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

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 };

題:

如何根據登錄用戶修改此代碼以呈現不同的側邊欄?

第一步高度依賴於您的身份驗證配置方式。 使用azure登錄后,您將希望將檢索到的用戶配置文件存儲在redux數據存儲中。

如果您將反應redux與adal一起使用,則應該調度登錄操作。 在此操作成功后,在您的登錄reducer中,從檢索到的信息中選擇您需要的內容。

以下代碼僅是實現此類reducer后可能留下的樣本。 AD肯定會為您提供一個組ID而不是此示例中顯示的“admin”角色,只需相應地修改檢查。 您可能必須在AAD的應用程序清單中將groupMembershipClaims設置為"SecurityGroup""All" ,以便在身份驗證響應中添加此信息。

case Action.LOGIN_SUCCESS:
  return {
    ...state,
    username: action.username,
    isAdmin: action.role === 'admin'
  }

您可能需要自定義AD從AD儀表板發送的信息。

剩下的是微不足道的:

  • 將商店連接到需要用戶權限的組件

     connect(state => ({ user: state.loginReducer, })) 
  • 在組件內有條件地自定義渲染

     render() { const { user } = this.props; return ( <div className={classNames.navbar}> { user.isAdmin && <Link to="adminpanel" label="Admin Panel" /> } <Link to="about" label="About" /> </div> ) } 

順便說一下,如果需要處理動態語言切換,還應該在redux存儲中設置語言環境。

在提供的代碼中,我沒有找到一種方法來確定用戶是否經過身份驗證。 我認為這是重點。

最簡單的方法是檢查cookie。 這當然取決於您的身份驗證的工作方式。 您需要有一個檢查auth cookie的函數。 像這樣的東西。 我正在使用universal-cookie包,看起來你的應用程序是同構的,所以你需要在服務器上傳遞ssrCookie ,你將從res.headers.cookies獲得。 如果應用程序只是客戶端,則可以刪除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)
 }

那么,現在,在您對代碼做出反應時,您會檢查用戶是否已登錄:

import { isLoggedIn } from './thePathToFileWhereThisFunctionsWasSet'

class Sidebar extends Component {
   render() {
     return (
       {isLoggedIn() ? (<div>For logged in user</div>) : (<div>For not logged in users</div>)}
     )
   }
}

在使用react-adal代碼挖掘一下之后,我發現logged狀態沒有通過子代的上下文傳遞。 所以,據我所知,獲得當前用戶的唯一方法是:

import { authContext } from './adalConfig';
authContext.getCachedUser();

這只是在你使用runWithAdal包裝主組件之后。

有了它,你可以創建一個新的組件SidebarOptions ,它將決定prop useruser = authContext.getCachedUser(); )要呈現哪些選項。

另一個選擇是創建一個PR react-adal並確保通過包裝組件或所有子s components (using傳遞這些值s components (using react-context`)

此外,我可以看到您正在使用redux,因此最好通過使用authContext.getCachedUser()數據在第一個呈現的組件上調度操作來保存用戶信息,並將其設置在您的商店中以便在整個應用程序中輕松訪問。 這樣做的好地方是withAdalLoginApi的成功回調。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM