[英]On React Router, how to stay logged in state even page refresh?
我正在使用 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; 博士
在前端,您有一个登录和注册路由,可根据服务器上的身份验证向您的服务器查询 JWT。 一旦通过了适当的 JWT,您就可以将 state 属性设置为 true。 您可以有一个注销路由,允许用户将此状态设置为 false。
包含路由的 index.js 可以在渲染前检查本地存储,从而消除刷新时丢失状态但保持一定安全性的问题。
在您的应用程序中需要身份验证的所有路由都通过组合组件呈现,并通过在服务器 API 上进行授权的标头中包含 JWT 的必要性来进行保护。
设置它需要一点时间,但它会使您的应用程序“合理”安全。
要解决您的问题:
检查index.js
文件中路由之前的本地存储,如下所示,如果需要,将状态更新为已验证。
该应用程序通过 JWT 保护 API 来维护安全性,这将解决您的刷新问题,并维护到您的服务器和数据的安全链接。
因此,在路线中你会有这样的事情:
索引.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
为真。
如果state.authenticated
为真,则组件,在本例中为Graph
和IsAuthenticated
。 否则默认返回到根路由。
然后你可以像这样构建一个组合组件,通过它呈现你的所有路由。 它将在渲染之前检查您持有的用户是否经过身份验证(布尔值)的状态是否为真。
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 是另一个步骤。 我发现护照是有效的。
为什么不使用具有登录状态和到期日期的 sessionStorage? 您将不得不编写更多代码来检查 sessionStorage 状态,但在我看来,这是您可以避免发送 XHR 调用的唯一方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.