Une autre façon de procéder est d'utiliser Jetons Web JSON (JWT) qui sont nécessaires pour chaque route, et localStorage pour vérifier la présence du JWT.
TL;DR
-
Sur le front, vous avez une route d'identification et d'inscription qui interroge votre site Web. serveur pour un JWT selon l'authentification sur le serveur. Une fois que passé le JWT approprié, vous pouvez alors définir une propriété de state à true. Vous pouvez avoir une route de sortie qui permet à l'utilisateur de définir cet état à false. état à false.
-
L'index.js qui contient vos routes peut vérifier le stockage local avant d'effectuer le rendu, éliminant ainsi votre problème de perte d'état lors du rafraîchissement mais en gardant une certaine sécurité.
-
Toutes les routes nécessitant une authentification dans votre application sont rendues par le biais d'un Composant Composé, et sécurisées avec la nécessité de d'avoir des JWTs dans l'en-tête pour l'autorisation sur l'API du serveur.
Cette configuration prend un peu de temps, mais elle rendra votre application "raisonnablement" sûre.
Pour résoudre votre problème :
Vérifiez le stockage local avant les itinéraires dans votre index.js
comme indiqué ci-dessous, en mettant à jour l'état à authentifié si nécessaire.
L'application maintient la sécurité grâce au fait que l'API est sécurisée par le JWT, ce qui résout votre problème de rafraîchissement et maintient un lien sécurisé avec votre serveur et vos données.
Ainsi, dans les itinéraires, 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 le composant composé tandis que Graph
et IsAuthenticated
(il peut s'agir de n'importe quel nombre de composants nommés de manière appropriée) nécessitent l'utilisation de l'option state.authenticated
pour être vrai.
Les composants, dans ce cas Graph
et IsAuthenticated
rendu si le state.authenticated
est vrai. Sinon, il revient par défaut à la route Root.
Vous pourriez alors construire un composant composé comme celui-ci, par lequel toutes vos routes sont rendues. Il vérifiera que l'état dans lequel vous maintenez si l'utilisateur est authentifié ou non (un booléen) est vrai avant d'effectuer 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/signature, vous pourriez créer une action qui stocke le JWT et configure l'état pour l'authentification via un créateur d'action -> redux store. Cet exemple fait usage d'axios pour exécuter le cycle asynchrone de réponse aux demandes 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!'));
});
};
}
Vous devrez également configurer votre magasin (Redux dans ce cas) et votre créateur d'actions, bien sûr.
La "vraie" sécurité vient de l'arrière. Pour ce faire, vous utilisez localStorage pour conserver le JWT sur le front-end et le transmettre dans l'en-tête de tout appel d'API contenant des informations sensibles/protégées.
La création et l'analyse syntaxique du JWT pour les utilisateurs sur l'API du serveur est une autre étape. J'ai trouvé que le passeport était efficace.