【问题标题】:How to dynamically use react routing如何动态使用react路由
【发布时间】:2021-05-08 23:30:44
【问题描述】:

我有一个应用程序,其中我有两个登录名,一个用于superAdmin,另一个用于“管理员”。

  1. 我有几个页面一个是常见的(主页),两个用户都有多余的。

  2. 然后我还有其他几个页面,其中一些是 admin,而另一些是 superAdmin

  3. 现在,当我打开我的页面时,我正试图走“/”这条路线(我的家路线)。 我想要做什么

  4. 现在,如果我以管理员身份登录并且管理员用户在地址栏中输入一些 superAdmin url,我希望将其重定向到当前管理员路由

  5. superAdmin 也一样

  6. 我想限制的两个用户都超出了彼此的路线

  7. 如果我是管理员用户或超级管理员用户并尝试登录并尝试过多的经过身份验证的路由,我应该被重定向到主页

我做了什么

我在这里创建了一个组件(动态路由),我正在检查用户正在尝试做什么。 在我的路由文件中的 route.js 中,我将 props 传递为 guest,superAdminadmin

Dynamicroute.js 代码

我已经创建了我的上下文来存储用户登录后

    export default function Dynamicroute(props) {
    const { user } = useAuthState();  // this I am getting from my context
    console.log(user);

    if (props.partner && !user) {
        console.log('admin not logedin');
        return <Redirect to="/admin" />;
    } else if (props.admin && !user) {
        console.log('superAdmin not loged in');
        return <Redirect to="/superAdmin" />;
    } else if (props.admin && user.role === 'admin') {
        console.log('admin logedin');
        return <Redirect to="/admin_home" />;
    } else if (props.admin && user.role === 'superAdmin') {
        console.log('super admin loged in');
        return <Redirect to="/superadmin_home" />;
    } else if (props.guest && user) {
        console.log('guest');
        return <Redirect to="/" />;
    } else {
        return <Route component={props.component} {...props} />;
    }
}

我的 route.js

<DuynamicRoute exact path="/" component={Home} guest />
<DuynamicRoute path="/admin" component={loginAdmin} guest />
<DuynamicRoute path="/superAdmin" component={loginSuperAdmin} guest />
<DuynamicRoute path="/admin_home" component={admin_home} admin/>
<DuynamicRoute path="/superAdmin_home" component={superAdmin_home} superAdmin/>

我面临的问题

我不知道我面临的问题是在登录时将我重定向到该路由但内容未加载 如果我在该页面上控制台某些内容,我无法获得该内容,则该页面将变为空白。

I am following this lecture from 25:00 timing

已编辑

Here is my code sand box

请检查一下

编辑

admin 和 super admin 将在不同的浏览器中登录,所以只是不希望 admin 访问 super admin,反之亦然,如果他们输入 url 对方的溃败

【问题讨论】:

  • 嘿,你可以尝试使用 Window.location,而不是返回 直接使用 window.location,因为我看到你正在使用 react,使用 window .location 将带你到正确的地方,让我知道 window.location 是否正常工作,我会发布一个完整的关于它的 sn-p :)
  • 您是否定义了/admin_home 路由?我可以看到您的 url 地址指向该路由,但我在这里的代码示例中没有看到它。
  • @froston 抱歉打错了,我编辑了答案
  • 请注意,component={props.component} 是无用的。 /admin_home 路由是否在您的应用程序中定义?
  • @Olivier 是的,当然它是定义的,实际上它甚至没有带我去任何只显示空白页面的路线。

标签: javascript reactjs react-router react-hooks react-router-dom


【解决方案1】:

为了更好地管理和开发程序以及 最佳实践,在 React.js 中按如下方式进行授权:

Demo on Codesandbox

首先:您需要一个class 来检查permissionsroutes/pages configs,如下所示:

class AppUtils {
  static setRoutes(config) {
    let routes = [...config.routes];

    if (config.auth) {
      routes = routes.map((route) => {
        let auth = config.auth ? [...config.auth] : null;
        auth = route.auth ? [...auth, ...route.auth] : auth;
        return {
          ...route,
          auth
        };
      });
    }

    return [...routes];
  }

  static generateRoutesFromConfigs(configs) {
    let allRoutes = [];
    configs.forEach((config) => {
      allRoutes = [...allRoutes, ...this.setRoutes(config)];
    });
    return allRoutes;
  }

  static hasPermission(authArr, userRole) {
    /**
     * If auth array is not defined
     * Pass and allow
     */
    if (authArr === null || authArr === undefined) {
      // console.info("auth is null || undefined:", authArr);
      return true;
    } else if (authArr.length === 0) {
      /**
       * if auth array is empty means,
       * allow only user role is guest (null or empty[])
       */
      // console.info("auth is empty[]:", authArr);
      return !userRole || userRole.length === 0;
    } else {
      /**
       * Check if user has grants
       */
      // console.info("auth arr:", authArr);
      /*
            Check if user role is array,
            */
      if (userRole && Array.isArray(userRole)) {
        return authArr.some((r) => userRole.indexOf(r) >= 0);
      }

      /*
            Check if user role is string,
            */
      return authArr.includes(userRole);
    }
  }
}

export default AppUtils;

第二:您需要授权组件来处理如下路由:

import React, { Component } from "react";
import AppUtils from "utils/AppUtils";
import { matchRoutes } from "react-router-config";
import { withRouter } from "react-router-dom";
import AppContext from "context/AppContext";

class AppAuthorization extends Component {
  constructor(props, context) {
    super(props);
    const { routes } = context;
    this.state = {
      accessGranted: true,
      routes
    };
  }

  componentDidMount() {
    if (!this.state.accessGranted) {
      this.redirectRoute();
    }
  }

  componentDidUpdate() {
    if (!this.state.accessGranted) {
      this.redirectRoute();
    }
  }

  static getDerivedStateFromProps(props, state) {
    const { location, userRole } = props;
    const { pathname } = location;

    const matched = matchRoutes(state.routes, pathname)[0];

    return {
      accessGranted: matched
        ? AppUtils.hasPermission(matched.route.auth, userRole)
        : true
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.accessGranted !== this.state.accessGranted;
  }

  redirectRoute() {
    const { location, userRole, history } = this.props;
    const { pathname, state } = location;
    const redirectUrl = state && state.redirectUrl ? state.redirectUrl : "/";

    /*
        User is guest
        Redirect to Login Page
        */
    if (!userRole || userRole.length === 0) {
      history.push({
        pathname: "/login",
        state: { redirectUrl: pathname }
      });
    } else {
      /*
        User is member
        User must be on unAuthorized page or just logged in
        Redirect to dashboard or redirectUrl
        */
      history.push({
        pathname: redirectUrl
      });
    }
  }

  render() {
    // console.info('App Authorization rendered', accessGranted);
    return this.state.accessGranted ? (
      <React.Fragment>{this.props.children}</React.Fragment>
    ) : null;
  }
}

// AppAuthorization.defaultProps = {
//   userRole: [] // You can manage roles by redux or any state managements
// };

AppAuthorization.contextType = AppContext;

export default withRouter(AppAuthorization);

第三:您需要 authRoles 文件或远程来管理客户端上的角色,如下所示:

/**
 * Authorization Roles
 */
const authRoles = {
  admin: ["admin"],
  superAdmin: ["superAdmin"],
  user: ["user"],
  onlyGuest: []
};

export default authRoles;

第四:如果你想继续这个逻辑,你必须实现你的页面结构如下:

src
 |---pages
      |---home
           |---HomePage.jsx
           |---HomePageConfig.jsx
      |
      |- The rest of the pages should be implemented in the same way

例如:当你想实现一个只有管理员才能看到的页面时(管理员主页配置):

import React from "react";
import authRoles from "auth/authRoles";

export const AdminHomePageConfig = {
  auth: authRoles.admin,
  routes: [
    {
      path: "/admin",
      exact: true,
      component: React.lazy(() => import("./HomePage"))
    }
  ]
};

或者大家都能看到的首页:

import React from "react";

export const HomePageConfig = {
  routes: [
    {
      path: "/",
      exact: true,
      component: React.lazy(() => import("./HomePage"))
    }
  ]
};

根据上面的示例,您可以在此处输入auth proprole,以限制对页面的访问。

为了更深入地了解这个逻辑,我在 Codesandbox 中实现了它,您可以通过以下链接查看:

Demo on Codesandbox

注意:上面的demo需要更完整,并且不要在状态中存储用户角色,最好使用状态管理 包(redux,...),还可以通过以下方式执行登录操作 饼干。

【讨论】:

  • 是的,我确实使用上下文 ali 进行状态管理和使用本地存储的登录过程,因为我正在使用客户端渲染,我会尝试你的方法,我正在使用带有钩子的功能组件。
【解决方案2】:

您可以创建一个检查身份验证并在进入路由时重定向的函数,而不是创建动态路由。

const yourRouter = () => {
  // yourAuthLogic
  const routeAuth = (Component, props) => {
  // redirect logic
  // here you use the if/else branching based on auth state to redirect  
  // if no redirect
  return (
    <Component {...props} />
  )
}

return (
  <Router>
    <Switch>
      <Route path="/admin" render={() => routeAuth(component, props)} />
      <Route path="/superAdmin" render={() => routeAuth(component, props)} />
   </Switch>
 </Router>
  )
}

【讨论】:

  • 这样,如果路由数量多,管理难度大,代码脏,也限制了配置能力。
【解决方案3】:

问题在于 DuynamicRoute 组件在其顶层返回一个重定向,但重定向不能直接在 Switch 组件内工作。这是因为 Switch 中的重定向会导致无限重定向循环。 要解决此问题,您应该从自定义 Route 组件返回顶级 Route,同时处理 Route 标签之间的重定向逻辑。

另外,值得一提的是,有些路由不应该是受特殊保护的路由,而是常规的登陆页面,例如主页和登录页面。

这是一个基于您的 CodeSandbox 解决方案的示例项目: https://codesandbox.io/s/vigilant-feather-jbq4j

我这样做是为了让 superAdmin 用户可以访问管理员级别,但反过来不行。如果不将活动用户更改为超级管理员,则小管理员无法访问超级管理员内容。

这是一个对逻辑稍作修改的附加链接,用于您希望管理员和超级管理员无法访问彼此受保护页面的用例:https://codesandbox.io/s/brave-haze-zsmn9?file=/src/ProtectedRoute.js

【讨论】:

  • 所以根据你Super admin 可以访问admin 但这不是我想要的,如果super admin 访问管理员不是正确的方式,因为管理员登录是 diff 和他们可以有不同的令牌,
  • @manish thakur 我添加了一个带有互斥逻辑示例的附加链接,其中管理员无法访问超级管理员页面,超级管理员也无法访问管理员页面。对 ProtectedRoute.js 文件进行了轻微修改。
【解决方案4】:

由于这还没有一个公认的答案,我将提出我用于根据条件添加/删除路由的简单方法。

示例使用 Typescript,但如果需要,删除类型应该相对容易。

条件路由组件:

此组件具有所有 Route 道具的完整类型以及一个名为 enabled 的附加道具。

启用为true时,路由将正常渲染,为false时将返回null。

// ConditionalRoute.tsx
import * as React from 'react';
import { Route, RouteProps } from 'react-router-dom';

interface ConditionalRouteProps extends RouteProps {
    enabled?: boolean;
}

const ConditionalRoute: React.FunctionComponent<ConditionalRouteProps> = ({
    enabled,
    ...routeProps
}) => {
    if (!enabled) {
        return null;
    }

    return (
        <Route {...routeProps} />
    );
};

export default ConditionalRoute;

条件重定向组件:

该组件具有所有 Redirect 道具的完整类型以及一个称为启用的附加道具。

当 enabled 为 true 时,重定向将正常执行,当为 false 时,将返回 null。

// ConditionalRedirect.tsx
import * as React from 'react';
import { Redirect, RedirectProps } from 'react-router-dom';

interface ConditionalRedirectProps extends RedirectProps {
    enabled?: boolean;
}

const ConditionalRedirect: React.FunctionComponent<ConditionalRedirectProps> = ({
    enabled,
    ...redirectProps
}) => {
    if (!enabled) {
        return null;
    }

    return (
        <Redirect {...redirectProps} />
    );
};

export default ConditionalRedirect;

使用条件组件:

像使用它们的原始基础组件一样使用条件路由/重定向,除非启用属性为 true,否则它们不会生效。

这也适用于 Switch 组件,因为它会忽略返回 null 的组件。

// App.tsx
import * as React from 'react';
import { BrowserRouter, Switch } from 'react-router-dom';

interface AppProps {
    authenticatedUser?: any;
}

const ConditionalRedirect: React.FunctionComponent<AppProps> = ({
    authenticatedUser
}) => {

    return (
        <BrowserRouter>
            <Switch>
                { 
                    // Always allow the unauthenticated error route
                }
                <Route 
                    path="/error/401" 
                    component={Unauthenticated} 
                />
                { 
                    // If user is authenticated allow member route
                }
                <ConditionalRoute 
                    enabled={!!authenticatedUser} 
                    path="/member" 
                    component={MemberHome} 
                />
                { 
                    // If user is an admin allow admin route
                }
                <ConditionalRoute 
                    enabled={authenticatedUser?.isAdmin} 
                    path="/administration" component={AdminHome} 
                />
                { 
                    // If user is an admin redirect uncaught route 
                    // to administration route
                }
                <ConditionalRedirect 
                    enabled={authenticatedUser?.isAdmin} 
                    path="/" 
                    to="/administration" 
                />
                { 
                    // If user is authenticated redirect uncaught route 
                    // to member route
                }
                <ConditionalRedirect 
                    enabled={!!authenticatedUser} 
                    path="/" 
                    to="/member" 
                />
                { 
                    // If user is not authenticated redirect uncaught 
                    // route to unauthenticated error route 
                }
                <ConditionalRedirect 
                    enabled={!authenticatedUser} 
                    path="/" 
                    to="/error/401" 
                />
            </Switch>
        </BrowserRouter>
    );
};

export default App;

此解决方案的主要警告是,条件中的任何值都必须在渲染任何路线之前准备好进行评估。例如,如果您发出异步请求以检查用户是否在上面的示例应用程序中经过身份验证,当用户在异步请求返回之前最初未定义时,它将重定向到 401 页面。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多