Як обмежити доступ до маршрутів у React-Router?


83

Хтось знає, як обмежити доступ до певних маршрутів у реакційному маршрутизаторі? Я хочу перевірити, чи ввійшов користувач в систему, перш ніж дозволити доступ до певного маршруту. Я думав, що це буде просто, але в документації незрозуміло, як це зробити.

Це щось я повинен встановити там, де я визначаю свої <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, які він завантажує, тому не зберігайте в ньому конфіденційну інформацію.
Полковник Тридцять два,

@Tanner Semerad чи є у вас репозиторій github про те, як ви цього коротко досягли.
jit

@jit я не знаю, вибачте. Відповідь від miciek нижче був те, що мені потрібно, але майте на увазі, що це було до реакції маршрутизатора 1.0. Я знаю, що низка речей змінилася з часу випуску 1.0, але це здебільшого схоже.
Таннер Семерад

Відповідь @ jayair - це те, що я використовую зараз, і це чудово працює
Таннер Семерад

Відповіді:


94

Оновлення (16 серпня 2019 р.)

У 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. Ми робимо це, коли наш додаток завантажується вперше. Також варто згадати, можливо, ви захочете додати знак завантаження у свій додаток під час запуску перевірки автентичності, тому ви не будете блимати сторінкою входу щоразу, коли ви оновлюєте сторінку.

Потім ми передаємо прапор на наші маршрути. Ми створюємо два типи маршрутів 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 вважаються шкідливими ( https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html ) командою React.

Якщо хтось стикається з цим питанням і шукає рекомендований спосіб зробити це, я б запропонував використовувати компоненти вищого порядку замість Mixins.

Ось приклад 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-checks-auth.html


Якби в моєму вихідному коді використовувався <Route getComponent = {myAsyncComponentGenerator}>, як би я змусив його працювати з цим прикладом?
Бран

У мене дуже подібний код, але моє питання полягає в тому, чи достатньо захищений? Я маю в виду , може бути зловмисник може змінити JS зменшеного код таким чином, що заміна this.props.isLoggedInз trueі байпасом Логіна?
karim elhelawy

4
@karimelhelawy Це правда, і тому вам потрібно примусити автентифікацію в API вашого сервера.
cbr

7
<Route handler={}/>застаріло у версії 1.0, вам слід використовувати <Route component={} />.
Знання

1
componentWillMountнезабаром будуть застарілими. Прочитайте це у дописі в блозі на реакції.org . Натомість я би пішов із відповіддю @jacob.
Том

29

Є (зараз?) Приклад цього в документах React Router 4 для Redirect

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

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

Як я можу використовувати "loggedIn" як функцію або змінну ?. Ви можете просто трохи це пояснити
Кунвар Сінгх,

@KunvarSingh це, мабуть, повинна бути функцією, оскільки значення змінюється.
jacob

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

Якщо ви хочете використовувати автентифікацію в усьому своєму додатку, вам потрібно зберегти деякі дані для всієї програми (наприклад, маркер). Ви можете встановити два міксини React, які відповідають за управління $authоб’єктом. Цей об’єкт не повинен бути доступним поза цими двома комбінаціями. Ось приклад цього:

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функцію виклику, коли у вас є всі необхідні дані.

Найголовніше - захистити свої компоненти змішуванням NeedsAuthenticatedUserміксину. Кожен компонент, якому потрібен аутентифікований користувач, повинен виглядати так:

var um = require('userManagement');

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

Зверніть увагу, що NeedsAuthenticatedUserвикористовується API-інтерфейс ( willTransitionToі transition.abort()).


2
Міксіни - погана ідея рухатися далі. Детальніше
boldnik

Я знайшов набагато кращий спосіб: github.com/reactjs/react-router/tree/master/examples/auth-flow
boldnik

1
Міксини були вилучені з 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 не пройшов, або як виглядає обробник "Main"? Наприклад, що, якщо "Огляд" має залежність, яка вимагає запуску автентифікованого додатка? Оскільки він імпортується для запуску на маршрутизаторі, усі його залежності також будуть імпортовані, і, отже, у вас зламана програма, чи не так?
Marais Rossouw

Це не відповідає на запитання, яке було задано
HermannHH
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.