我正在用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')
);

请看代码,如果用户未登录,我使用OneNet钩子重定向到"/login"路径.用于判断用户是否登录的数据位于存储中,并且在用户登录后将更新.

它工作得很好,但问题是当我刷新页面时,存储被重置,用户没有重新登录状态.

我知道这是因为Redux存储只是内存存储,所以刷新页面会丢失存储中的所有数据.

在每次刷新时判断服务器会话可能会起作用,但这可能是太多的请求,所以这似乎是个坏主意.

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

有没有办法更简单地解决这个问题?我的网站需要处理大量用户,所以我希望尽可能减少XHR呼叫.

任何建议都将不胜感激.

推荐答案

另一种方法是使用每条路由所需的JSON Web Tokens (JWT)个,使用localStorage个来判断JWT.

TL;DR

  • 在前端,您有一条登录和注册路由,可以查询您的

  • 索引.包含路由的js可以判断本地存储

  • 将呈现应用程序中需要身份验证的所有路由

设置这个需要一点时间,但它会使你的应用程序"合理"安全.


To solve your problem:

Check the local storage before the routes in your 100 file as shown below, updating the state to authenticated if required.

该应用程序维护了API的安全性,因为该API由JWT保护,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是合成组件,而GraphIsAuthenticated(可以是任意数量的适当命名组件)要求state.authenticated为真.

如果state.authenticated为真,则渲染组件(在本例中为GraphIsAuthenticated).否则默认返回根路由.


然后,您可以构建一个这样的组合组件,通过它可以呈现所有路由.在呈现之前,它将判断您持有的用户是否经过身份验证(布尔值)的状态是否为真.

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的操作,并通过操作创建者将状态设置为已验证->;reduxstore .下面的示例演示如何运行异步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!'));
      });
  };
} 

当然,你还需要建立你的store (本例中是Redux)和action creator.

真正的安全来自后端.要做到这一点,您可以使用localStorage将JWT保留在前端,并将其在头中传递给任何具有敏感/受保护信息的API调用.

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

Reactjs相关问答推荐

如何在useEffect中使用useParams()

在Reaction中测试条件组件

REACT路由DOM根据参数呈现不同的路由

可选链和useState挂钩

如何在取数后添加新数据,并在页面上保存旧数据?

捕获表单数据时的Reactjs问题

TanStack/Reaction-Query在我的Vercel应用程序中不起作用

获取类别和页面的参数

为 Next.js 应用程序目录中的所有页面添加逻辑的全局文件是什么?

为什么在 React 的 useEffect 中使用 onAuthStateChanged ?

useMemo 依赖项的行为

React Router 6 点击链接在 React 18 应用中打开 flickr 页面

Firebase查询返回随机用户数据,而不是过滤后的用户数据

ReactJS中使用setState方法时,Context内部的State未进行更新

当我在 React Router Dom 中使用两个参数并直接在页面上导航时,数据获取没有发生

如何在 React map 函数循环中使用
标签

Reactjs 表单未反映提交时的更改

我刚刚部署了我的 Vite React 站点,但我的图标/图像没有部署

交换键仍然引入了 fresh 的 DOM 元素

ClearInterval 在 React 中的 useRef 没有按预期工作