React:private et public route

folder Projet: test_react_project

Afin de sécuriser l'accès à des components on peut interdire l'accès si l'utilisateur n'est pas connectée ou son rôle ne lui permet pas.

Installation :

npm install react-private-route --save
npm install react-cookie --save
npm install react-router-dom --save

Exemple

Dans ce type on va créer et sécuriser l'accès à des components selon le rôle de chaque utilisateur
├── Role:Admin
│     ├─ Facture
│     ├─ Achat
├── Role:User
│     ├─ Achat
├── public
│     ├─ Home
│     ├─ Login
│     ├─ NotFound

1.Modifier App.js en ajoutant le code suivant

import React,{useEffect,useState} from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Routes, Link ,Navigate,} from "react-router-dom";
import PrivateRoute from "react-private-route";
import { useCookies } from "react-cookie";

import 'bootstrap/dist/css/bootstrap.min.css';

import Login from './components/login/login.js';
import Home from './components/home/home.js';
import Achat from './components/achat/achat.js';
import Facture from './components/facture/facture.js';
import NotFound from './components/notFound/notFound.js';
import Forbiden from './components/forbiden/forbiden.js';





function App() {
/*****1.Varibales ***/
/*un tableau de role et les components qui peut y accéder*/
const roles = [
{id: 1, role: 'Admin', components:[ 'Facture','Achat']},
{id: 2, role: 'User', components:['Achat']}
];


/*auth:Varaible de state qui permet de récupérer login,password et le role From les cookies
afin de vérifier est ce que l'utilisateur est connecté ou non
*/
const [auth,setAuth]=useState({login:'',password:'',role:''});

/*cookies:Variable de state qui permet de récupérer la cookie auth sauvegarder s'il existeCréer*/
const [cookies, setCookie,removeCookie] = useCookies(["auth"]);
/*estConnect:Varaible de state qui stock l'état de la connexion ,connecte true or false*/
const [estConnect,setEstConnect]=useState(false);




/******2:Gestion des routes private *****/
/*{children} :les components dont PrivateRoute est la mère
...props}:récupérer les propos envoyé lorsque on appell <PrivateRoute data>
*/
const PrivateRoute = ({ children,...props}) => {

/*
<PrivateRoute role="User"> ==>Private Route
<Achat name="Achat" /> ==>Child
</PrivateRoute>
*/

/*récupérer le role assoicé à la privateRoute */
const childRole=props.role;
/*récupérer le nom de la component à afficher */
const childName=children.props.name;

/*Utilisateur est déjà connecté */
if(cookies.auth!=undefined)
{
/*contiendra true si l'utilisteur connecté à le droit d'affficher la component childName*/
let hasRole=false;
/*boucler sur le tableau d'objet roles afin de chercher la est ce que :
cookies.auth.role :le role de l'utilisateur connecté a le droit d'afficher la component ChildName
{id: 1, role: 'Admin', components:[ 'Facture','Achat']},
*/
roles.forEach((r) => {
if((cookies.auth.role==r.role)&& r.components.includes(childName) )
{
/*Ok il a le droit*/
hasRole=true;
}
});

/*Ok il a le droit donc on afficher le children =>la component à afficher*/
if(hasRole)
{
return children
}

/*si non on fait une redirection vers forbiden
Car l'utilisateur est connecté JUSTE il n'a pas le droit d'afficher cette Child*/
else
{
return <Navigate to="/forbiden" state="Vous n'avez pas le droit" />
}
}
/*Fin if utilisateur est connecté*/

/*Si il n'est pas Connecté redirect vers la connexion*/
return <Navigate to="/login" state="Erreur il faut se connecter" />
}


/***3.Charger l'utilisateur from les cookies ****
/*Une fois l'application est lancée on Vérifier si l'utilisateur est déjà connecté */
useEffect(() => {
/*si le cookies('auth) existe */
if(cookies.auth!=undefined)
{
setAuth(cookies.auth);
setEstConnect(true);
}
}, []);



/*Déconnexion :Vider le cookie auth */
const deconnexion=()=>{
removeCookie("auth");
setEstConnect(false);
/*Rediection vers la route /login en envoyant state vide */
return <Navigate to="/login" state="" />
}


/*View*/
return (
<div>
{/* 1.Le NAV */}
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="collapse navbar-collapse">
<div className="navbar-nav">
<a className="nav-item nav-link " href="/">Home :Public</a>
<a className="nav-item nav-link " href="/Achat">Acheter</a>
<a className="nav-item nav-link " href="/facture">Facture</a>
</div>
{estConnect==true &&
<span class="nav-item nav-link">
BienVenue:{auth.login} :Role {cookies.auth.role}
<input onClick={deconnexion} className="btn btn-success " type="button" value="Deconnexion"/>
</span>
}
</div>
</nav>

{/*2. Les routes*/}
<Router>
<Routes>
{/*Les routes publiques*/}
<Route path="/login" element={<Login /> }/>
<Route path="/" element={<Home /> } />
<Route path="/forbiden" element={<Forbiden /> } />
{/*Les routes privées*/}
<Route path="/achat" element={
<PrivateRoute role="User">
<Achat name="Achat" />
</PrivateRoute>
}
/>
<Route path="/facture" element={
<PrivateRoute role="Admin">
<Facture name="Facture" />
</PrivateRoute>
}
/>
{/*NotFound*/}
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
</div>);
}
export default App;

Créer la component login.js

npx crcf components/login
import React, { Component,useState,useEffect } from "react";
import { useCookies } from "react-cookie";
import { useNavigate,useLocation,useSearchParams } from "react-router-dom";


function Login(){

/*Créer une state qui sera utilisé dans le formulaire*/
const [auth,setAuth]=useState({login:'',password:'',role:''});

/*Créer un compte et le stock dans les cookies */
const [cookies, setCookie] = useCookies(["auth"]);

const navigate = useNavigate();
const location = useLocation();


const inputChange = e => {
/*Récupérer l'input changé*/
const { name, value } = e.target;
if(value!='')
{
/*Modifier dans le state l'attirbut Client changé*/
setAuth(prevAuth => ({
...prevAuth,/*Récupérer l'ancien objet*/
[name]: value /*changé la valeur de l'attribut :<input name="login" ou name="password"*/
}));
}
};


/*Sauvegarder les preferences dans les cookies */
const connexion=()=> {
if(auth.login!='' && auth.password!='')
{
setCookie("auth", auth,
{
path: "/"
});
/*Une fois connecté redirect vers Home*/
navigate("/");
}
}



/*Vérifier si l'utilisateur est déjà connecté */
useEffect(() => {
if(cookies.auth!=undefined)
{
setAuth(cookies.auth);
console.log("not null"+cookies.auth.login);
}
}, []);



/*Afficher l'interface*/

return (
<div className="container">
<br/>
<div className="card text-success card bg-light mb-3">
<div className="card-header">Connexion</div>
<div className="card-body">
<input value={auth.login} onChange={e => inputChange(e)} name="login" className="form-control" type="text" placeholder="Nom"/>
<input value={auth.password} onChange={e => inputChange(e)} name="password" className="form-control" type="text" placeholder="Prenom"/>
<select value={auth.role} onChange={e => inputChange(e)} name="role" className="form-control">
<option value="">Choisir un Role</option>
<option value="Admin">Admin</option>
<option value="User">User</option>
</select>
<input onClick={connexion} className="form-control btn btn-success " type="button" value="Envoyer"/>
</div>
</div>

<div class="card text-white bg-danger">{location.state}</div>
</div>
);
}

export default Login;

Créer la component Home.js

npx crcf components/home
import React from 'react';

function Home(props) {
return (
<div className="container">
<br/>
<div className="card text-success card bg-light mb-3">
<div className="card-header">Hi Je suis Home</div>
<div className="card-body">
<h2>Je suis public</h2>
</div>
</div>
</div>
)
}
export default Home;

Créer la component notFound.js

npx crcf components/notFound
import React from 'react';

function NotFound(props) {
return (
<div className="container">
<br/>
<div className="card text-dark mb-3">
<div className="card-header card bg-warning">Hi Je suis NotFoud</div>
<div className="card-body">
<h2>Je suis public</h2>
</div>
</div>
</div>
)
}
export default NotFound;

Créer la component forbiden.js

npx crcf components/forbiden
import React, { Component,useState,useEffect } from "react";
import { useCookies } from "react-cookie";
import { useNavigate,useLocation,useSearchParams } from "react-router-dom";


function Forbiden(){

/*Récuper le message envoyer par la route:
<Navigate to="/forbiden" state="Vous n'avez pas le droit" />
*/
const location = useLocation();

/*Afficher l'interface*/
return (
<div className="container">
<br/>
<div className="card mb-3">
<div className="card-header text-white card bg-danger">Fobiden for your</div>
<div className="card-body">
Message from parent :<b> {location.state}</b>
</div>
</div>
</div>
);
}

export default Forbiden;

Créer la component achat.js

npx crcf components/achat
import React from 'react';

function Achat(props) {
return (
<div className="container">
<br/>
<div className="card text-dark mb-3">
<div className="card-header card bg-warning">Hi Je suis Achat</div>
<div className="card-body">
<h2>Only User and Admin</h2>
</div>
</div>
</div>
)
}
export default Achat;

Créer la component facture.js

npx crcf components/facture
import React from 'react';

function Facture(props) {
return (
<div className="container">
<br/>
<div className="card text-dark mb-3">
<div className="card-header card bg-warning">Hi Je suis Facture</div>
<div className="card-body">
<h2>Only Admin</h2>
</div>
</div>
</div>
)
}
export default Facture;