简体   繁体   English

如何限制对反应路由器中路由的访问?

[英]How to restrict access to routes in react-router?

Does anyone know how to restrict access to particular routes in react-router?有谁知道如何限制对 react-router 中特定路由的访问? I want to check if the user is logged in before allowing access to a particular route.我想在允许访问特定路线之前检查用户是否已登录。 I thought it would be simple, but the docs aren't clear how to do it.我认为这很简单,但文档不清楚如何去做。

Is this something I should set up where I define my <Route> components, or should I be handling it inside my component handlers?这是我应该在定义我的<Route>组件的地方设置的东西,还是应该在我的组件处理程序中处理它?

<Route handler={App} path="/">
  <NotFoundRoute handler={NotFound} name="not-found"/>
  <DefaultRoute handler={Login} name="login"/>
  <Route handler={Todos} name="todos"/> {/* I want this to be restricted */}
</Route>

Update (Aug 16, 2019)更新(2019 年 8 月 16 日)

In react-router v4 and using React Hooks this looks a little different.在 react-router v4 和使用 React Hooks 中,这看起来有点不同。 Let's start with your App.js .让我们从您的App.js开始。

export default function App() {
  const [isAuthenticated, userHasAuthenticated] = useState(false);

  useEffect(() => {
    onLoad();
  }, []);

  async function onLoad() {
    try {
      await Auth.currentSession();
      userHasAuthenticated(true);
    } catch (e) {
      alert(e);
    }
  }

  return (
    <div className="App container">
      <h1>Welcome to my app</h1>
      <Switch>
        <UnauthenticatedRoute
          path="/login"
          component={Login}
          appProps={{ isAuthenticated }}
        />
        <AuthenticatedRoute
          path="/todos"
          component={Todos}
          appProps={{ isAuthenticated }}
        />
        <Route component={NotFound} />
      </Switch>
    </div>
  );
}

We are using an Auth library to check if the user is currently authenticated.我们正在使用Auth库来检查用户当前是否已通过身份验证。 Replace this with your auth check function.将其替换为您的身份验证检查功能。 If so then we set the isAuthenticated flag to true .如果是,那么我们将isAuthenticated标志设置为true We do this when our App first loads.我们在应用程序首次加载时执行此操作。 Also worth mentioning, you might want to add a loading sign on your app while the auth check is being run, so you don't flash the login page every time you refresh the page.另外值得一提的是,您可能希望在运行身份验证检查时在您的应用程序上添加一个加载标志,这样您就不会在每次刷新页面时都刷新登录页面。

Then we pass the flag to our routes.然后我们将标志传递给我们的路线。 We create two type of routes AuthenticatedRoute and UnauthenticatedRoute .我们创建了两种类型的路由AuthenticatedRouteUnauthenticatedRoute

The AuthenticatedRoute.js looks like this. AuthenticatedRoute.js看起来像这样。

export default function AuthenticatedRoute({ component: C, appProps, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        appProps.isAuthenticated
          ? <C {...props} {...appProps} />
          : <Redirect
              to={`/login?redirect=${props.location.pathname}${props.location.search}`}
            />}
    />
  );
}

It checks if isAuthenticated is set to true .它检查isAuthenticated是否设置为true If it is, then it'll render the desired component.如果是,那么它将呈现所需的组件。 If not, then it'll redirect to the login page.如果没有,那么它将重定向到登录页面。

The UnauthenticatedRoute.js on the other hand looks like this.另一方面, UnauthenticatedRoute.js看起来像这样。

export default ({ component: C, appProps, ...rest }) =>
  <Route
    {...rest}
    render={props =>
      !appProps.isAuthenticated
        ? <C {...props} {...appProps} />
        : <Redirect to="/" />}
  />;

In this case, if the isAuthenticated is set to false , it'll render the desired component.在这种情况下,如果isAuthenticated设置为false ,它将呈现所需的组件。 And if it is set to true, it'll send you to the homepage.如果它设置为 true,它会将您发送到主页。

You can find detailed versions of this on our guide - https://serverless-stack.com/chapters/create-a-route-that-redirects.html .您可以在我们的指南中找到详细版本 - https://serverless-stack.com/chapters/create-a-route-that-redirects.html

Older version旧版本

The accepted answer is correct but Mixins are considered to be harmful ( https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html ) by the React team.接受的答案是正确的,但 React 团队认为 Mixins 是有害的( https://facebook.github.io/react/blog/2016/07/13/mixins-thinked-harmful.html )。

If somebody comes across this question and is looking for the recommended way to do this, I'd suggest using Higher Order Components instead of Mixins.如果有人遇到这个问题并正在寻找推荐的方法来做到这一点,我建议使用高阶组件而不是 Mixins。

Here is an example of a HOC that'll check if the user is logged in before proceeding.这是一个 HOC 的示例,它将在继续之前检查用户是否已登录。 And if the user is not logged in, then it'll redirect you to the login page.如果用户未登录,那么它会将您重定向到登录页面。 This component takes a prop called isLoggedIn , that is basically a flag that your application can store to denote if the user is logged in.该组件采用一个名为isLoggedIn ,它基本上是您的应用程序可以存储的一个标志,用于表示用户是否已登录。

import React from 'react';
import { withRouter } from 'react-router';

export default function requireAuth(Component) {

  class AuthenticatedComponent extends React.Component {

    componentWillMount() {
      this.checkAuth();
    }

    checkAuth() {
      if ( ! this.props.isLoggedIn) {
        const location = this.props.location;
        const redirect = location.pathname + location.search;

        this.props.router.push(`/login?redirect=${redirect}`);
      }
    }

    render() {
      return this.props.isLoggedIn
        ? <Component { ...this.props } />
        : null;
    }

  }

  return withRouter(AuthenticatedComponent);
}

And to use this HOC, just wrap it around your routes.要使用这个 HOC,只需将它包裹在你的路线上。 In case of your example, it would be:在您的示例中,它将是:

<Route handler={requireAuth(Todos)} name="todos"/>

I cover this and a few other topics in a detailed step-by-step tutorial here - https://serverless-stack.com/chapters/create-a-hoc-that-checks-auth.html我在这里的详细分步教程中介绍了这个和其他一些主题 - https://serverless-stack.com/chapters/create-a-hoc-that-c​​hecks-auth.html

There is (now?) an example of this in React Router 4's docs for Redirect在 React Router 4 的Redirect文档中有(现在?)一个例子

import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

react-router encourages a declarative approach for your router, you should make your router as dumb as possible and avoid putting your routing logic in your components. react-router鼓励为您的路由器采用声明式方法,您应该使您的路由器尽可能笨拙,并避免将您的路由逻辑放在您的组件中。

Here is how you can do it (assuming you pass it the loggedIn prop):以下是您的操作方法(假设您将其传递给了loggedIn道具):

const DumbRouter = ({ loggedIn }) => (
  <Router history={history}>
    <Switch>
      {[
        !loggedIn && LoggedOutRoutes,
        loggedIn && LoggedInRouter,
        <Route component={404Route} />
      ]}
    </Switch>
  </Router>
);

const LoggedInRoutes = [
  <Route path="/" component={Profile} />
];

const LoggedOutRoutes = [
  <Route path="/" component={Login} />
];

If you want to use authentication across your whole application, you need to store some data application-wide (eg token).如果要在整个应用程序中使用身份验证,则需要在应用程序范围内存储一些数据(例如令牌)。 You can set up two React mixins that are responsible for managing $auth object.您可以设置两个负责管理$auth对象的 React mixin。 This object shouldn't be available outside those two mixins.该对象不应该在这两个 mixin 之外可用。 Here's example of that:下面是一个例子:

define('userManagement', function() {
    'use strict';

    var $auth = {
        isLoggedIn: function () {
            // return something, e.g. using server-stored data
        }
    };

    return {
        Authenticator: {
           login: function(username, password) {
               // modify $auth object, or call server, or both
           }
        },

        NeedsAuthenticatedUser: {
            statics: {
                willTransitionTo: function (transition) {
                    if (!$auth.isLoggedIn()) {
                        transition.abort();
                    }
                }
            }
        }
    };
});

Then you can just mixin Authenticator mixing to your login components (login screen, login popup, etc) and call this.login function when you have all the data necessary.然后,您可以将Authenticator混合到您的登录组件(登录屏幕、登录弹出窗口等)中,并在您拥有所有必要的数据时调用this.login函数。

The most important thing is protecting your components by mixing in NeedsAuthenticatedUser mixin.最重要的是通过混合NeedsAuthenticatedUser混合来保护您的组件。 Each component that needs authenticated user will have to look like that:每个需要经过身份验证的用户的组件都必须如下所示:

var um = require('userManagement');

var ProtectedComponent = React.createClass({
    mixins: [um.NeedsAuthenticatedUser]
    // ...
}

Note that NeedsAuthenticatedUser uses react-router API ( willTransitionTo and transition.abort() ).请注意, NeedsAuthenticatedUser使用 react-router API( willTransitionTotransition.abort() )。

You can use HOC and auth is a variable you can change value true or false means(authorization)您可以使用 HOC 并且 auth 是一个变量,您可以更改值 true 或 false 表示(授权)

<Route path="/login" component={SignIn} />
<Route path="/posts" render = {() => (auth ?  (<Post />) : (<Redirect to="/login" />))}/>

private-route.tsx私有路由.tsx

import {Redirect, Route, RouteProps} from 'react-router';
import * as React from 'react';

interface PrivateRouteProps extends RouteProps {
  /**
   * '/login' for example.
   */
  redirectTo: string;

  /**
   * If true, won't redirect.
   * We are using a function instead of a bool, a bool does not seem to be updated
   * after having successfully authenticated.
   */
  isLogged: () => boolean;
}


export function PrivateRoute(props: PrivateRouteProps) {
  // `component: Component` is not typing, it assign the value to a new variable.
  let { isLogged, redirectTo, component: Component, ...rest }: any = props;

  // error: JSX type element Component does not have call signature or ... AVOIDED BY ADDING ANY, still work,
  // and did not find a proper way to fix it.
  return <Route {...rest} render={(props) => (
    isLogged()
      ? <Component {...props}/>
      : <Redirect to={{
        pathname: redirectTo,
        state: { from: props.location }
      }} />
  )} />;
}

Usage:用法:

        <PrivateRoute exact={true} 
                      path="/admin/" 
                      redirectTo={'/admin/login'} 
                      isLogged={this.loginService.isLogged} 
                      component={AdminDashboardPage}/>
        <Route path="/admin/login/" component={AdminLoginPage}/>

Based on https://tylermcginnis.com/react-router-protected-routes-authentication/ .基于https://tylermcginnis.com/react-router-protected-routes-authentication/

You can avoid to render component before confirming authentication, like as below:您可以避免在确认身份验证之前渲染组件,如下所示:

import { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';

const Route = () => {
    const [loading, sertLoading] = useState(true);
    const history = useHistory();

    const ref = useRef<Function>({});

    // must use ref!
    ref.current.routeGuard = () => {
        const authenticationHandler = (): boolean => {
         // do authentication here
        }
        sertLoading(true);
        const go = authenticationHandler();
        if (go === false) {
            history.goBack();
        }
        sertLoading(false);
    } 

    useEffect(() => {
        ref.current.routeGuard();
        history.listen(() => {
            ref.current.routeGuard();
        });
    }, []);

    return (
        <>
            {!loading && <YourRouteComponent />}
        </>
    )
};

Or simply, yarn add react-routers , which component have props beforeEach , beforeRoute like Vue Route.或者简单地说, yarn add react-routers ,其中组件有道具beforeEachbeforeRoute像 Vue Route。

usually a logged in user will be granted a token, and uses this token for any communication with server.通常,登录用户将被授予一个令牌,并使用此令牌与服务器进行任何通信。 What we usually do is define a root page, and things build on top of that page.我们通常做的是定义一个根页面,然后建立在该页面之上。 this root page does localisation, authentication and other configurations for you.此根页面为您进行本地化、身份验证和其他配置。

here's an example这是一个例子

Routes = (
    <Route path="/" handler={Root}>
        <Route name="login" handler={Login} />
        <Route name="forget" handler={ForgetPassword} />
        <Route handler={Main} >
            <Route name="overview" handler={Overview} />
            <Route name="profile" handler={Profile} />
            <DefaultRoute handler={Overview} />
        </Route>
        <DefaultRoute handler={Login} />
        <NotFoundRoute handler={NotFound} />
    </Route>
);

on your root page, check for token null or authenticate the token with server to see if user is valid login.在您的根页面上,检查令牌是否为空或使用服务器验证令牌以查看用户是否有效登录。

hope this helps :)希望这可以帮助 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM