Sur le routeur React, comment rester connecté dans l'état même rafraîchir la page?
je fais le site Web avec le routeur React and React avec Redux. Beaucoup de routes(pages) nécessitent une connexion. Je peux rediriger vers de connexion si l'utilisateur n'est pas connecté comme ceci:
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')
);
s'il vous Plaît voir le code, j'ai utilisé onEnter crochet pour rediriger vers '/login' itinéraire si l'utilisateur n'est pas connecté. Les données pour vérifier l'utilisateur connecté est dans le magasin, et il sera mis à jour après l'utilisateur connecté.
Il fonctionne parfaitement, mais le problème est lorsque j'actualise la page, le magasin sera réinitialisé et l'utilisateur n'est pas enregistré dans l'état de retour.
je sais que cela se produit parce que Redux store est juste un stockage de mémoire, donc la page de refesh va perdre toutes les données.
Vérifiez la session du serveur sur chaque rafraîchissement peut être du travail, mais cela pourrait être trop de requête, de sorte que cela semble mauvaise idée.
enregistrer les données de l'état connecté dans localStorage pourrait être du travail, mais dans ce cas, je devrais vérifier chaque appel AJAX échoue que la demande rejetée parce que la session est expirée ou n'existe pas comme quelque chose, et il semble que mauvaise idée non plus.
Est-il un moyen de résoudre ce problème plus clairement? Mon site web va utiliser beaucoup de gens, donc je veux réduire les appels XHR que possible.
tout conseil sera très apprécié.
2 réponses
une Autre façon est d'utiliser JSON Web Tokens (JWT) qui sont nécessaires pour chaque route, et localStorage pour vérifier la JWT.
TL;DR
à l'avant, vous avez une route d'inscription et d'inscription qui interroge votre serveur pour une JWT selon l'authentification sur le serveur. Lorsque passé le JWT approprié vous mettriez alors une propriété d'État à vrai. Vous pouvez avoir une route de signalisation qui permet à l'utilisateur de définir ce l'état false.
l'index.js qui contient vos itinéraires peut vérifier le stockage local avant de rendre, éliminant ainsi votre problème avec la perte de l'état sur refresh mais en gardant un peu de sécurité.
toutes les routes nécessitant une authentification dans votre application sont rendues par un composant composé, et garanti par la nécessité de avoir JWTs dans l'en-tête pour autorisation sur le serveur API.
cela prend un peu de temps, mais cela rendra votre application "raisonnablement" Sécuritaire.
Pour résoudre ton problème:
vérifiez le stockage local avant les routes dans votre index.js
fichier comme indiqué ci-dessous, mise à jour de l'État à authentifier si nécessaire.
l'application maintient la sécurité avec le fait que L'API est sécurisée par le JWT qui résoudrait votre problème de rafraîchissement, et maintenir un lien sécurisé vers votre serveur et les données.
ainsi dans les routes vous auriez quelque chose comme ceci:
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
est la composante composée alors que Graph
et IsAuthenticated
(peut être n'importe quel nombre de composants correctement nommés) nécessite le state.authenticated
pour être vrai.
Les Composants, dans ce cas Graph
et IsAuthenticated
rendu si le state.authenticated
est vraie. Autrement est par défaut retour à la route des racines.
alors vous pouvez construire un composant composé comme celui-ci, à travers lequel toutes vos routes sont rendues. Il vérifiera que l'état dans lequel vous tenez que l'utilisateur est authentifié ou non (un booléen) est vrai avant le rendu.
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);
}
du côté de l'inscription vous pouvez créer une action qui stocke la JWT et met en place l'état de authentifié par un magasin action-creator -> redux. Cet exemple utilise axios pour exécuter le cycle de réponse de la requête HTTP async.
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!'));
});
};
}
vous devez également configurer votre boutique (Redux dans ce cas) et action creator bien sûr.
la "vraie" sécurité vient de l'arrière. Et pour ce faire, vous utilisez localStorage pour garder le JWT sur le devant et le passer dans l'en-tête à tous les appels API qui ont sensible / protégé information.
créer et analyser le JWT pour les utilisateurs sur L'API du serveur est une autre étape. j'ai trouvé le passeport efficace.
pourquoi ne pas utiliser sessionStorage avec l'état enregistré et la date d'expiration? Vous devrez écrire plus de code pour vérifier l'état sessionStorage, mais c'est la seule façon, à mon avis, de sauver l'appel XHR de l'envoi.