我正在使用 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
是组合组件,而 Graph
和 IsAuthenticated
(可以是任意数量的适当命名的组件)需要 状态。已验证
为真。
如果 state.authenticated
为真,组件,在本例中为 Graph
和 IsAuthenticated
呈现。否则默认返回根路由。
然后您可以像这样构建一个组合组件,通过它呈现您的所有路由。它将在呈现之前检查您所持有的用户是否已通过身份验证( 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/