javascript - 在 React Router 上,如何保持登录状态甚至页面刷新?

标签 javascript reactjs react-redux react-router local-storage

我正在使用 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' 路由。用于检查用户是否登录的数据在商店中,它将在用户登录后更新登录。

它工作得很好,但问题是当我刷新页面时,商店被重置并且用户没有登录状态。

我知道发生这种情况是因为 Redux 存储只是内存存储,所以刷新页面会丢失存储中的所有数据。

在每次刷新时检查服务器 session 可能有效,但这可能是请求太多,所以这似乎是个坏主意。

将登录状态数据保存到 localStorage 可能有效,但在这种情况下,我应该检查每个 AJAX 调用是否失败,因为 session 已过期或不存在等请求被拒绝,这似乎也是个坏主意。

有没有更简单的方法来解决这个问题?我的网站需要处理大量用户,因此我想尽可能减少 XHR 调用。

任何建议将不胜感激。

最佳答案

另一种方法是使用 JSON Web Tokens (JWT)每条路线都需要,localStorage检查 JWT。

长话短说

  • 在前端,您有一个登录和注册路由来查询您的 根据服务器上的身份验证为 JWT 服务器。一次 通过了适当的 JWT,然后您可以将状态属性设置为 真的。您可以有一个注销路由,允许用户设置它 状态为假。

  • 包含您的路由的 index.js 可以检查本地存储 在渲染之前,从而消除了丢失状态的问题 刷新但保持一定的安全性。

  • 在您的应用程序中呈现所有需要身份验证的路由 通过组合组件,并根据需要进行保护 在服务器 API 的 header 中包含 JWT 以进行授权。

设置它需要一些时间,但它会使您的应用程序“相当”安全。


解决您的问题:

检查 index.js 文件中路由之前的本地存储,如下所示,如果需要,将状态更新为已验证。

该应用程序通过 API 受 JWT 保护这一事实来维护安全性,这将解决您的刷新问题,并维护与您的服务器和数据的安全链接。

因此在 route 你会有这样的东西:

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>
  , document.getElementById('entry'));

RequiredAuth 是组合组件,而 GraphIsAuthenticated(可以是任意数量的适当命名的组件)需要 状态。已验证为真。

如果 state.authenticated 为真,组件,在本例中为 GraphIsAuthenticated 呈现。否则默认返回根路由。


然后您可以像这样构建一个组合组件,通过它呈现您的所有路由。它将在呈现之前检查您所持有的用户是否已通过身份验证( bool 值)的状态是否为真。

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

在注册/登录方面,您可以创建一个操作来存储 JWT 并设置状态以通过 action-creator -> redux store 进行身份验证。本例 makes use of 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)和 action creator。

“真正的”安全来自后端。为此,您使用 localStorage 将 JWT 保留在前端,并将其在 header 中传递给任何具有敏感/ protected 信息的 API 调用。

在服务器 API 上为用户创建和解析 JWT 是另一个步骤。 I have found passport to be effective.

关于javascript - 在 React Router 上,如何保持登录状态甚至页面刷新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39097440/

相关文章:

javascript - 按 id 引用下拉列表不同组件的数组

javascript - Redux 中的选择器 - React 组件了解 redux 状态意味着什么?

javascript - 为包含 id 数组的对象编写 Redux Actions 和Reducers 到其他对象

javascript - 如何使用纯Javascript检查单击的元素是否具有背景属性?

reactjs - Flow (React Native) 使用 'this.state' 时出现错误

javascript - 在 react 中将 window.addEventListener 添加到 componentDidMount 方法是否正确?

javascript - componentWillReceiveProps 包含多个 if

javascript - 为什么 goog.crypt.base64.decodeStringToByteArray(string) 在这里返回无效的字节数组?

javascript - 如何将 JPEG 从 URL 保存到 angularjs(ionic) 中的文件?

javascript - 如何等待在 map 内解决的 promise ?