当前位置: 首页 > 面试题库 >

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

公羊新
2023-03-14
问题内容

我正在使用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呼叫。

任何建议将不胜感激。


问题答案:

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

TL; DR

  • 在前端,您具有登录和注册路由,该路由根据服务器上的身份验证向服务器查询JWT。一旦通过适当的JWT,您便可以将state的属性设置为true。您可以有一个退出路线,该路线允许用户将此状态设置为false。

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

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

进行设置需要一些时间,但是它将使您的应用程序“合理地”安全。

解决您的问题:

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

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

因此,在路线中您将具有以下内容:

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是组成的组件,而Graphand
IsAuthenticated(可以是任意数量的适当命名的组件)都要求state.authenticatedtrue。

组件,在这种情况下GraphIsAuthenticated 如果state.authenticatedtrue则呈现。否则默认返回到根路由。

然后,您可以构建一个这样的组合组件,通过它来呈现所有路线。在呈现之前,它将检查您所持有的用户是否已通过身份验证(布尔值)的状态为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是另一步骤。我发现护照有效。



 类似资料:
  • 这是views.py的内容: 在转到“/”时,我被重定向到“/accounts/login”,它将我带到登录页面。我输入用户名和密码后,在“验证”打印语句printd“登录”到终端。 到目前为止还不错。现在,我不再被重定向到“/”,而是再次被重定向到“/accounts/login”,并再次显示输入用户名和密码的页面。为什么?

  • 问题内容: Redux文档的第一个原理是: 整个应用程序的状态存储在单个存储中的对象树中。 我实际上以为我很好地理解了所有原则。但是我现在对应用程序的含义感到困惑。 我知道,如果应用程序只是网站中复杂的小部分之一,并且仅用一页即可工作。但是,如果申请意味着整个网站怎么办?我应该使用LocalStorage还是cookie或其他保留状态树的东西?但是如果浏览器不支持LocalStorage怎么办?

  • 问题内容: 安装组件时,我想根据用户登录的状态来呈现登录屏幕或应用程序。但是,每次刷新时,用户都会注销。我如何保持他们的登录状态? 应用组件: 这是我用来登录用户的方法(效果很好): 问题答案: 用户的令牌自动保存到本地存储中,并在加载页面时读取。这意味着,当您重新加载页面时,应该再次自动验证用户身份。 最可能的问题是您的代码未检测到此身份验证,因为构造函数在Firebase重新加载并验证用户凭据

  • 假设我有以下简单的。 子菜单展开后,即使在页面刷新后,也应该保持展开,直到显式折叠为止。 类似地,稍后,如果子菜单被折叠,则应该保持折叠,直到页面重新加载/刷新后显式展开。这可能吗? 如果的第一个子菜单是折叠的,则以下脚本将展开该子菜单。

  • 我的应用程序使用Firebase API进行用户身份验证,将登录状态保存为Vuex状态下的布尔值。 当用户登录时,我设置登录状态并相应地有条件地显示登录/注销按钮。 但是当页面刷新时,vue应用的状态丢失并重置为默认 这会导致一个问题,即使用户登录并刷新页面,登录状态也会被设置为false,并且显示登录按钮而不是注销按钮,即使用户保持登录状态... 我该怎么做才能防止这种行为 我应该使用cooki

  • 问题内容: 我有一个内置于firebase和angular中的应用程序,并且希望能够在刷新页面后使用户保持登录状态。现在我有一个登录屏幕,其中包含两个基本输入字段,这些输入字段绑定到控制器 这一切都很好,很不错,并且可以,但是当用户刷新页面时,他们将被注销。有什么方法可以在加载控制器时查看用户是否已经登录并自动重定向?谢谢! 问题答案: 您现在拥有的代码可以处理用户登录的情况。要处理用户已经登录的