Подтвердить что ты не робот

На React Router, как оставаться в состоянии регистрации, даже обновить страницу?

Я делаю сайт с React и React Router с Redux. Требуется множество маршрутов (страниц). Я могу перенаправить на логин, если пользователь не зарегистрирован следующим образом:

function requireAuth(nextState, replace) {
    let loggedIn = store.getState().AppReducer.UserReducer.loggedIn;

    if(!loggedIn) {
        replace({
            pathname: '/login',
            state: {
                nextpathname: nextState.location.pathname
            }
        });
    }
}

ReactDOM.render(
    <Provider store={store}>
        <Router history={history}>
            <Route path="/" component={App}>
                <IndexRoute component={Index} />
                <Route path="login" component={Login} />
                <Route path="register" component={Register} />
                <Route path="dashboard" component={Graph} onEnter={requireAuth}>
                    ... some other route requires logged in ...
                </Route>
            </Route>
        </Router>
    </Provider>,
    document.getElementById('entry')
);

Пожалуйста, см. код, я использовал onEnter hook для перенаправления на маршрут "/login", если пользователь не вошел в систему. Данные для проверки входа пользователя в систему находятся в хранилище, и он будет обновляться после входа пользователя в систему.

Он работает отлично, но проблема в том, что когда я обновляю страницу, хранилище будет reset, и пользователь не войдет в состояние назад.

Я знаю, что это происходит, потому что хранилище Redux - это просто память, поэтому страница refesh потеряет все данные.

Проверка сеанса сервера при каждом обновлении может работать, но это может быть слишком большой запрос, так что выглядит плохой идеей.

Сохранять зарегистрированные данные состояния в localStorage, возможно, будет работать, но в этом случае я должен проверить, не вызваны ли все вызовы AJAX, что запрос отклонен, потому что сеанс истек или не существует как что-то, и это также выглядит плохой идеей.

Есть ли способ более четко решить эту проблему? Мой сайт будет использовать много людей, поэтому я хочу как можно скорее уменьшить XHR-запросы.

Любые советы будут очень оценены.

4b9b3361

Ответ 1

Еще один способ - использовать JSON Web Tokens (JWT), которые требуются для каждого маршрута, и localStorage, чтобы проверить JWT.

TL; DR

  • На переднем конце у вас есть signin и signup маршрут, который запрашивает ваш сервер для JWT в соответствии с аутентификацией на сервере. однажды передал соответствующий JWT, вы должны установить свойство состояния для правда. У вас может быть маршрут выписки, который позволяет пользователю установить этот состояние false.

  • В index.js, который содержит ваши маршруты, можно проверить локальное хранилище перед рендерингом, тем самым устраняя вашу проблему с потерей состояния при обновлении, но сохраняя некоторую безопасность.

  • Все маршруты, требующие аутентификации в вашем приложении, отображаются через Компоновный Компонент, и обеспеченный необходимостью имеющих JWT в заголовке для авторизации в API сервера.

Настройка этого занимает немного времени, но это сделает ваше приложение "разумным" безопасным.


Чтобы решить вашу проблему:

Проверьте локальное хранилище перед маршрутами в вашем файле index.js, как показано ниже, при необходимости обновив состояние для аутентификации.

Приложение поддерживает безопасность с тем, что API защищен JWT, который позволит решить проблему обновления и поддерживать безопасную ссылку на ваш сервер и данные.

Таким образом, в маршрутах у вас будет что-то вроде этого:

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import reduxThunk from 'redux-thunk';
import { AUTHENTICATE_THE_USER } from './actions/types';
import RequireAuth from './components/auth/require_auth';
import reducers from './reducers';

/* ...import necessary components */

const createStoreWithMiddleware = compose(applyMiddleware(reduxThunk))(createStore);

const store = createStoreWithMiddleware(reducers);

/* ... */

// Check for token and update application state if required
const token = localStorage.getItem('token');
if (token) {
    store.dispatch({ type: AUTHENTICATE_THE_USER });
}

/* ... */

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <Route path="/" component={App}>
        <IndexRoute component={Index} />
        <Route path="login" component={Login} />
        <Route path="register" component={Register} />
        <Route path="dashboard" component={RequireAuth{Graph}} />
        <Route path="isauthenticated" component={RequireAuth(IsAuthenticated)} />
        ... some other route requires logged in ...
      </Route>
    </Router>
  </Provider>
  , .getElementById('entry'));

RequiredAuth является составным компонентом, а Graph и IsAuthenticated (может быть любым числом соответствующих им компонентов) требует, чтобы state.authenticated был истинным.

Компоненты, в этом случае Graph и IsAuthenticated отображаются, если значение state.authenticated равно true. В противном случае по умолчанию возвращается корневой маршрут.


Затем вы можете создать составной компонент, подобный этому, через который отображаются все ваши маршруты. Он проверит, что состояние, в котором вы держите/нет аутентификацию пользователя (логическое), истинно перед рендерингом.

require_auth.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

export default function (ComposedComponent) {

  // If user not authenticated render out to root

  class Authentication extends Component {
    static contextTypes = {
      router: React.PropTypes.object
    };

    componentWillMount() {
      if (!this.props.authenticated) {
        this.context.router.push('/');
      }
    }

    componentWillUpdate(nextProps) {
      if (!nextProps.authenticated) {
        this.context.router.push('/');
      }
    }

    render() {
      return <ComposedComponent {...this.props} />;
    }
  }

  function mapStateToProps(state) {
    return { authenticated: state.authenticated };
  }

  return connect(mapStateToProps)(Authentication);
}

На стороне signup/signin вы можете создать действие, которое хранит JWT и настроит состояние для аутентификации через хранилище action-creator → redux. В этом примере использует axios для запуска цикла ответа на запрос асинхронного HTTP-запроса.

export function signinUser({ email, password }) {

  // Note using the npm package 'redux-thunk'
  // giving direct access to the dispatch method
  return function (dispatch) {

    // Submit email and password to server
    axios.post(`${API_URL}/signin`, { email, password })
      .then(response => {
        // If request is good update state - user is authenticated
        dispatch({ type: AUTHENTICATE_THE_USER });

        // - Save the JWT in localStorage
        localStorage.setItem('token', response.data.token);

        // - redirect to the route '/isauthenticated'
        browserHistory.push('/isauthenticated');
      })
      .catch(() => {
        // If request is bad show an error to the user
        dispatch(authenticationError('Incorrect email or password!'));
      });
  };
} 

Вам также нужно будет настроить свой магазин (в этом случае Redux) и создатель действия.

"Настоящая" безопасность исходит из задней части. И для этого вы используете localStorage, чтобы держать JWT в интерфейсе и передавать его в заголовке на любые вызовы API с конфиденциальной/защищенной информацией.

Создание и анализ JWT для пользователей API-интерфейса сервера - еще один шаг. Я нашел паспорт, чтобы быть эффективным.

Ответ 2

Почему бы не использовать sessionStorage с зарегистрированным состоянием и сроком действия? Вам нужно будет написать еще код для проверки состояния sessionStorage, но единственный способ, по-моему, вы можете сохранить вызов XHR от отправки.