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

IT技术 javascript reactjs react-router
2021-02-28 11:51:37

有谁知道如何限制对 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>
6个回答

更新(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-thinked-harmful.html)。

如果有人遇到这个问题并且正在寻找推荐的方法来做到这一点,我建议使用高阶组件而不是 Mixins。

这是一个 HOC 的示例,它将在继续之前检查用户是否已登录。如果用户未登录,那么它会将您重定向到登录页面。该组件采用一个名为 的propsisLoggedIn,这基本上是您的应用程序可以存储的一个标志,用于表示用户是否已登录。

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

我有非常相似的代码,但我的问题是,是否足够安全?我的意思是可以是攻击者可以更改JS精缩代码,使得替换this.props.isLoggedIntrue和旁路登录?
2021-04-23 11:51:37
componentWillMount很快就会被弃用。在 reactjs.org 上的博客文章中阅读它相反,我会使用@jacob 提供的答案。
2021-04-26 11:51:37
<Route handler={}/>在 v1.0 中已弃用,您应该使用<Route component={} />.
2021-05-01 11:51:37
如果我的原始代码使用 <Route getComponent={myAsyncComponentGenerator}>,我将如何使它与这个示例一起工作?
2021-05-08 11:51:37
@karimelhelawy 确实如此,因此您需要在服务器的 API 中强制执行身份验证。
2021-05-08 11:51:37

在 React Router 4 的文档中有(现在?)一个例子 Redirect

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

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>
我如何使用“loggedIn”作为函数或变量?。你能解释一下吗
2021-05-02 11:51:37
@KunvarSingh 它可能应该是一个函数,因为值发生了变化。
2021-05-17 11:51:37

react-router 鼓励为您的路由器采用声明式方法,您应该使您的路由器尽可能笨拙,并避免将您的路由逻辑放在您的组件中。

这是您的方法(假设您将loggedInprops传递给它):

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 个不同的位置创建相同的 Route,这很难维护。
2021-04-30 11:51:37

如果要在整个应用程序中使用身份验证,则需要在应用程序范围内存储一些数据(例如令牌)。你可以设置两个负责管理$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混合到您的登录组件(登录屏幕、登录弹出窗口等)并this.login在您拥有所有必要的数据时调用函数。

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

var um = require('userManagement');

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

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

Mixins 已从 ES6 中移除,React 已弃用它们。
2021-04-21 11:51:37
2021-05-07 11:51:37
混合是继续前进的坏主意。阅读更多
2021-05-20 11:51:37

您可以使用 HOC 并且 auth 是一个变量,您可以更改值 true 或 false 意味着(授权)

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