Как я могу показать другую боковую панель на основе информации о пользователе? После входа в систему. ReactJS - PullRequest
0 голосов
/ 27 июня 2018

У меня есть приложение activ js, которое аутентифицируется с помощью активного каталога Azure, а затем показывает это меню:

enter image description here

Однако я хочу, чтобы после входа в систему на основе информации, полученной из Azure AD в виде ролей или групп, отображалась другая боковая панель с различными параметрами, поэтому у меня может быть одно и то же приложение с различными группами пользователей, и меню будет зависит от роли или группы.

Это структура моего приложения:

enter image description here

А вот и соответствующие файлы:

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

Вопрос:

Как изменить этот код для отображения другой боковой панели в зависимости от пользователя, вошедшего в систему?

Ответы [ 3 ]

0 голосов
/ 12 июля 2018

В представленном коде я не нашел способа выяснить, аутентифицирован ли пользователь или нет. Я думаю, что это главное.

Самый простой способ сделать это - проверить файл cookie. Это, конечно, зависит от того, как работает ваша аутентификация. У вас должна быть функция, которая будет проверять подлинность 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>)}
     )
   }
}
0 голосов
/ 14 июля 2018

Немного покопавшись в коде react-adal, я увидел, что состояние logged не передается через детский контекст. так что, насколько я вижу, единственный способ получить текущего пользователя:

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

Это только после того, как вы завернули свой главный компонент с runWithAdal, как вы это сделали.

С этим вы можете создать новый компонент SidebarOptions, который определит опору user (user = authContext.getCachedUser();), какие опции рендерить.

Другой вариант - создать PR для react-adal и обязательно передать эти значения через обернутый компонент или через все дочерние элементы s components (using реагировать-контекст`)

Кроме того, как я вижу, вы используете redux, поэтому лучше сохранить информацию о пользователе, отправив действие на первый обработанный компонент с данными authContext.getCachedUser() и установить его в своем магазине для быстрого доступа через приложение. хорошее место для этого - обратный вызов withAdalLoginApi.

0 голосов
/ 27 июня 2018

Первый шаг в значительной степени зависит от того, как настроена ваша аутентификация. После того, как вы вошли в систему с помощью Azure, вы захотите сохранить полученный профиль пользователя в своем хранилище избыточных данных.

Если вы используете реагировать на редакцию с adal, вам необходимо отправить действие для входа в систему. В случае успеха этого действия, в вашем редукторе входа в систему, выберите то, что вам нужно, из полученной информации.

Следующий код является лишь примером того, что вы можете оставить после реализации такого редуктора. AD, безусловно, даст вам идентификатор группы вместо роли «admin», показанной в этом примере, просто измените проверку соответствующим образом. Возможно, вам придется установить groupMembershipClaims на "SecurityGroup" или "All" в манифесте вашего приложения в AAD, чтобы добавить этот фрагмент информации в ответ аутентификации.

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

Между прочим, вы должны установить локаль внутри избыточного хранилища, а также, если вам нужно обрабатывать динамическое переключение языков.

...