我正在使用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存储只是内存存储,所以refesh页面会丢失所有数据。
每次刷新时检查服务器会话都可以正常工作,但这可能是请求太多,所以看起来不太好。
将登录状态数据保存到localStorage可能会有效,但在这种情况下,我应该检查每个AJAX调用是否因为会话过期或不存在而拒绝该请求,这看起来也不错。
有没有办法更清楚地解决这个问题?我的网站将会使用很多人,所以我希望尽可能减少XHR呼叫。
非常感谢任何建议。
答案 0 :(得分:36)
另一种方法是使用每条路线所需的JSON Web Tokens (JWT),并localStorage检查JWT。
<强> TL; DR 强>
在前端,您有一个登录和注册路径,可以查询您的 根据服务器上的身份验证,JWT的服务器。一旦 通过适当的JWT,然后你将一个属性设置为 真正。您可以使用允许用户设置此选项的注销路线 陈述为假。
包含路由的index.js可以检查本地存储 在渲染之前,从而消除了丢失状态的问题 刷新但保持一定的安全性。
呈现您的应用程序中需要身份验证的所有路由 通过组合组件,并在必要时进行保护 在标题中包含JWT以便在服务器API上进行授权。
设置此项需要一些时间,但它会使您的应用程序合理地#39;安全
解决您的问题:
检查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
是组合的组件,而Graph
和IsAuthenticated
(可以是任意数量的适当命名的组件)要求state.authenticated
为真。
如果Graph
为真,则会在{@ 1}}和IsAuthenticated
中呈现组件。否则默认返回根路由。
然后你可以像这样构建一个Composed组件,通过它来渲染所有的路由。在渲染之前,它将检查您持有的状态(无论用户是否经过身份验证(布尔值))。
<强> require_auth.js 强>
state.authenticated
在注册/注册方面,您可以创建一个存储JWT的动作,并通过动作创建者设置要通过身份验证的状态 - &gt; redux商店。此示例makes use of axios用于运行异步HTTP请求响应周期。
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);
}
您还需要设置商店(本例中为Redux)和行动创建者。
真实的&#39;安全来自后端。为此,您可以使用localStorage将JWT保留在前端,并将其传递给具有敏感/受保护信息的任何API调用。
为服务器API上的用户创建和解析JWT是另一个步骤。 I have found passport to be effective.
答案 1 :(得分:1)
为什么不将sessionStorage与登录状态和到期日期一起使用?您将不得不编写更多代码来检查sessionStorage状态,但这是我认为您可以保存XHR呼叫的唯一方法。