如何在react-router中限制对路由的访问?


81

有谁知道如何在react-router中限制对特定路由的访问?我想在允许访问特定路由之前检查用户是否已登录。我以为这很简单,但是文档尚不清楚该怎么做。

这是我应该在定义<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>

如果未登录,请重定向到登录处理程序。还要注意,客户端可以访问它加载的所有JS,因此请不要在其中存储敏感信息。
上校22年

@Tanner Semerad您是否有任何有关如何实现此目标的github存储库。
jit

@jit我不知道,对不起。下面来自miciek的答案是我需要的,但请记住,这早于react-router 1.0。我知道自1.0发行以来,许多事情已经发生了变化,但是大部分都差不多。
Tanner Semerad

@jayair的答案是我现在正在使用的工具,它工作得很好
Tanner Semerad

Answers:


93

更新(2019年8月16日)

在react-router v4和使用React Hooks中,这看起来有些不同。让我们从开始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>
  );
}

我们正在使用一个Auth库来检查用户当前是否已通过身份验证。将其替换为您的身份验证检查功能。如果是这样,则将isAuthenticated标志设置为true。我们在应用程序首次加载时执行此操作。同样值得一提的是,您可能希望在运行身份验证检查时在应用程序上添加加载符号,因此您不必在每次刷新页面时都刷新登录页面。

然后我们将标志传递到路线。我们创建两种类型的路线AuthenticatedRouteUnauthenticatedRoute

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}`}
            />}
    />
  );
}

它检查是否isAuthenticated设置为true。如果是的话,它将渲染所需的组件。如果不是,那么它将重定向到登录页面。

UnauthenticatedRoute.js另一方面,看起来是这样的。

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

在这种情况下,如果将isAuthenticated设置为false,它将呈现所需的组件。如果将其设置为true,它将把您带到主页。

您可以在我们的指南中找到其详细版本-https: //serverless-stack.com/chapters/create-a-route-that-redirects.html

旧版

可接受的答案是正确的,但React团队认为Mixins是有害的(https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html)。

如果有人遇到此问题,并正在寻找推荐的方法来执行此操作,则建议使用高阶组件而不是Mixins。

这是一个HOC的示例,该HOC会在继续操作之前检查用户是否已登录。如果用户未登录,它将把您重定向到登录页面。该组件采用一个名为的道具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);
}

要使用此HOC,只需将其包装在您的路线周围即可。以您的示例为例:

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

我在这里的详细分步教程中介绍了此主题和其他一些主题-https: //serverless-stack.com/chapters/create-a-hoc-that-c​​hecks-auth.html


如果我的原始代码使用的是<Route getComponent = {myAsyncComponentGenerator}>,那么如何在本示例中使用它?
米糠

我有非常相似的代码,但是我的问题是,是否足够安全?我的意思是说,可能是攻击者可以更改JS缩小代码,从而替换this.props.isLoggedIntrue并且绕过登录?
karim elhelawy

4
@karimelhelawy是的,因此您需要在服务器的API中强制执行身份验证。
cbr

7
<Route handler={}/>在v1.0中已弃用,应使用<Route component={} />
知识

1
componentWillMount即将被弃用。在reactjs.org上的博客文章中阅读它。相反,我会使用提供的答案@jacob。
汤姆(Tom)

28

React Router 4的文档中有一个(现在?)的示例,用于 Redirect

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

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

如何使用“ loggedIn”作为函数或变量?您能解释一下
吗?Kunvar Singh

@KunvarSingh它可能应该是一个函数,因为值会更改。
jacob

2

如果要在整个应用程序中使用身份验证,则需要在应用程序范围内存储一些数据(例如令牌)。您可以设置两个负责管理$auth对象的React mixin 。在这两个mixin之外,该对象不可用。这是示例:

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

然后,您可以在需要所有数据时Authenticator将mixin混合到您的登录组件(登录屏幕,登录弹出窗口等)中并调用this.login函数。

最重要的是通过混合混合来保护您的组件NeedsAuthenticatedUser。每个需要经过身份验证的用户的组件都必须如下所示:

var um = require('userManagement');

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

请注意,它NeedsAuthenticatedUser使用react-router API(willTransitionTotransition.abort())。


2
Mixins是继续前进的坏主意。阅读更多
boldnik '16


1
Mixins已从ES6中删除,React已弃用它们。
2016年

2

react-router 如果您鼓励路由器采用声明式方法,则应使路由器尽可能地笨拙,并避免将路由逻辑放入组件中。

这是您的操作方法(假设您将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} />
];

这很简单,很好。事实是,无论您是注销还是登录,通常都希望识别相同的路由,因此,如果用户已注销,则可以正确地重定向到登录名。通常,您希望路由相同,但根据登录状态以不同的方式运行。同样,在您的解决方案中,您将通过在难以维护的2个不同位置创建相同的路由来添加重复项。
Rafael Porras Lucena


0

private-route.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 }
      }} />
  )} />;
}

用法:

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

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


-2

通常,已登录的用户将被授予令牌,并将该令牌用于与服务器的任何通信。我们通常要做的是定义一个根页面,然后在该页面的顶部进行构建。该根页面为您执行本地化,身份验证和其他配置。

这是一个例子

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

在您的根页面上,检查令牌是否为null或使用服务器对令牌进行身份验证,以查看用户是否有效登录。

希望这可以帮助 :)


2
正确,因此,如果Auth未通过,我将如何停止要导入的“概述”类,或者“主”处理程序是什么样的?例如,如果“概述”具有需要经过身份验证的应用程序才能运行的依赖项,该怎么办?因为它是导入来在路由器上运行的,所以它的所有依赖项也都已经导入了,因此您的应用程序坏了吗?
Marais Rossouw,2015年

这不能回答所提出的问题
HermannHH'7
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.