react-routerのルートへのアクセスを制限するにはどうすればよいですか?


83

誰かが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にアクセスできるため、機密情報をクライアントに保存しないでください。
大佐32

@Tanner Semeradこれを簡単に達成した方法について、githubリポジトリはありますか。
jit 2016年

@jitしません、ごめんなさい。以下のmiciekからの回答は私が必要としていたものでしたが、これはreact-router1.0より前であったことに注意してください。1.0がリリースされてから多くの変更があったことは知っていますが、ほとんど同じです。
タナーセメラッド2016年

@jayairの答えは私が今使っているものであり、それはうまく機能します
Tanner Semerad 2017

回答:


94

更新(2019年8月16日)

react-routerv4とReactHooksを使用すると、これは少し異なります。あなたのから始めましょう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ます。これは、アプリが最初に読み込まれるときに行います。また、認証チェックの実行中にアプリに読み込みサインを追加して、ページを更新するたびにログインページをフラッシュしないようにすることもできます。

次に、フラグをルートに渡します。2種類のルートAuthenticatedRouteとを作成しますUnauthenticatedRoute

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)にあります

古いバージョン

受け入れられた答えは正しいですが、MixinsはReactチームによって有害であると見なされます(https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html)。

誰かがこの質問に出くわし、これを行うための推奨される方法を探している場合は、Mixinの代わりに高次コンポーネントを使用することをお勧めします。

これは、続行する前にユーザーがログインしているかどうかを確認する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


元のコードが<RoutegetComponent = {myAsyncComponentGenerator}>を使用していた場合、この例でどのように機能させるのでしょうか。
ブラン

私は非常によく似たコードを持っていますが、私の質問は、十分に保護されていますか?つまり、攻撃者がJSの縮小コードを変更して、ログインに置き換えthis.props.isLoggedIntrueバイパスすることができるのではないでしょうか。
karim elhelawy 2017年

4
@karimelhelawyこれは事実であり、そのため、サーバーのAPIで認証を実施する必要があります。
cbr 2017年

7
<Route handler={}/>v1.0では非推奨です<Route component={} />。を使用する必要があります。
知識

1
componentWillMount間もなく非推奨になります。reactjs.orgのブログ投稿でそれを読んでください。代わりに、@ jacobが提供する答えを使用します。
トム

29

React Router 4のドキュメントに(今?)この例があります。 Redirect

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

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

「loggedIn」を関数または変数として使用するにはどうすればよいですか?少し説明していただけますか
Kunvar Singh 2018

@KunvarSingh値が変わるため、おそらく関数であるはずです。
jacob 2018年

3

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つの異なる場所に同じルートを作成することで、複製を追加しています。
ラファエルポラスルセナ

2

アプリケーション全体で認証を使用する場合は、アプリケーション全体(トークンなど)にデータを保存する必要があります。$authオブジェクトの管理を担当する2つのReactミックスインを設定できます。このオブジェクトは、これら2つのミックスイン以外では使用できないようにする必要があります。その例を次に示します。

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-routerAPI(willTransitionToおよびtransition.abort())を使用することに注意してください。


2
Mixinは先に進むのは悪い考えです。続きを読む
boldnik 2016年

私が見つけたはるかに良い方法:github.com/reactjs/react-router/tree/master/examples/auth-flow
boldnik

1
MixinはES6から削除され、Reactはそれらを非推奨にしました。
桟橋

1

HOCを使用でき、authは値を変更できる変数ですtrueまたはfalseは意味します(承認)

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

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が完了していない場合、インポートされる「Overview」クラスを停止するにはどうすればよいですか、または「Main」ハンドラーはどのように見えますか?たとえば、「概要」に、認証済みアプリの実行を必要とする依存関係がある場合はどうなりますか?ルーターで実行するためにインポートされているため、すべての依存関係もインポートされているため、アプリが壊れていますか?
Marais Rossouw 2015

これは尋ねられた質問に答えません
HermannHH 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.