在React Router上,即使页面刷新,如何保持登录状态?

问题描述 投票:0回答:3

我正在使用 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钩子重定向到'/login'路由。用于检查用户是否登录的数据在存储中,并且会在用户登录后更新.

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

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

每次刷新时检查服务器会话可能会起作用,但这可能会导致太多请求,因此这似乎是一个坏主意。

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

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

任何建议将不胜感激。

javascript reactjs react-redux react-router local-storage
3个回答
67
投票

另一种方法是使用每个路由所需的 JSON Web 令牌 (JWT),并使用 localStorage 检查 JWT。

TL;博士

  • 在前端,您有一个登录和注册路线,可以查询您的 根据服务器上的身份验证来获取 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>
  , document.getElementById('entry'));

RequiredAuth
是组合组件,而
Graph
IsAuthenticated
(可以是任意数量的适当命名的组件)要求
state.authenticated
为 true。

如果

Graph

 为 true,则渲染组件,在本例中为 
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); }


在注册/登录方面,您可以创建一个存储 JWT 的操作,并将状态设置为通过操作创建器 -> 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 调用。

在服务器 API 上为用户创建和解析 JWT 是另一个步骤。

我发现护照有效。


0
投票
为什么不使用具有登录状态和到期日期的sessionStorage?您必须编写更多代码来检查 sessionStorage 状态,但在我看来,这是避免发送 XHR 调用的唯一方法。


0
投票
成功登录后,您可以将个人资料详细信息、令牌存储在本地存储中。当页面刷新时,您可以从本地存储检索数据。这样,您就不必在每次页面刷新时强制用户重新登录。

一个缺点是 Web 本地存储数据可以永远存在,即使在服务器重新启动或页面退出后也是如此。但是,您可以使用令牌过期来管理它。

© www.soinside.com 2019 - 2024. All rights reserved.