Projekt

Obecné

Profil

React autentizace » Historie » Revize 15

Revize 14 (Jiří Trefil, 2023-04-23 10:11) → Revize 15/17 (Jiří Trefil, 2023-04-23 10:17)

h1. Autentizace na straně klienta (React aplikace) 

 h2. Popis logiky autentizace 

 * Uživatel se přihlásí pod svým účtem.  

 * Klientská aplikace pošle tento požadavek na SPADe aplikaci. Pokud je tento požadavek validní, tj. uživatel opravdu existuje a heslo je správné, pak je zpět odeslán JWT token (https://jwt.io/introduction). 

 * Klientská aplikace si tento token přečte a uloží jej do cookie. Tento token je od tohoto bodu posílán s *každým* klientským požadavkem na server v hlavičce, viz obrázek hlavičky níže.  
 !clipboard-202304231139-vx5qh.png! 
 
 * Pouze takto je uživatel považován za autentikováno a může se svobodně pohybovat po aplikaci bez omezení. 



 h1. Knihovna *react-auth-kit* 

 * K implementaci této feature byla použita knihovna třetí strany. Knihovna spravuje celý stav autentizace na straně klienta. 
 * Po přihlášení uživatele tedy knihovna uloží jwt token do cookie a následně poskytne 3 hooky (konkrétně useState), které s tímto tokenem manipulují. 
 * Následuje ukázka kódu, která používá knihovnu pro přihlášení uživatele. 

 h3. Přihlášení uživatele 

 Ukázka kódu představuje první hook: *UseSignIn*. 

 <pre><code class="react"> 
 //importujeme useEffect knihovny pro prihlaseni uzivatele 
 import {useSignIn} from "react-auth-kit"; 

 //komponenta pro prihlaseni uzivatele 
 const LoginComponent = () => { 
     //initneme funkci knihovny 
     //ktera udela vsechnu praci s ukladanim tokenu 
     const signIn = useSignIn(); 

     //dummy funkce, ktera pouze demonstruje, jak se knihovna pouziva 
     //samotna implementace v kodu je v jadru stejna, pouze je kolem vice omacky 
     //pro tuto ukazku nedulezite 
     const sampleLogin = async (username:string, password: string) =>{ 
         //V tomto bode zavolame server s nasim login pozadavkem 
         //odpoved serveru je ve formatu jako nasledujici rozepsany objekt 
         //tedy vratime nam status (HTTP code) - tim zjistime, jestli se neco nepokazilo 
         //zpravu (message) - pro blizsi informace o vysledku informace 
         //samotny jwt token (token) - timto tokenem se uzivatel autentikuje s kazdym nasledujicim pozadavkem na server 
         const responseWrapper: {message:string; status:number; token:string} = await login(username,password); 
         //token muze byt empty string, pokud doslo k chybe prihlaseni 
         const accessToken:string = responseWrapper.token; 
         if(responseWrapper.status != 200){ 
         //Zde by byla cast, ktera by vypsala uzivateli chybovou hlasku, protoze se prihlaseni nepovedlo 
           
         } 
         //volame samotny useEffect pro ulozeni tokenu do cookie 
         signIn({ 
             //token, ktery ukladame, v nasem pripade promenna accessToken 
             // cookie bude mit nazev "token" 
             token:accessToken, 
             //jak dlouho token zije (v minutach) 
             expiresIn: 3600, 
             //typ tokenu, v pripade JWT se jedna o Bearer token 
             tokenType: "Bearer", 
             // co dalsiho si chceme ulozit, napriklad jmeno uzivatele, nutne to tu byt nemusi 
             authState: {userName:username} 
         }); 

        
     } 


 } 
 export default LoginComponent; 
 </code></pre> 

 h3. Kontrola, zda je uživatel autentikován 

 Tato ukázka kódu představuje druhý hook: *useIsAuthenticated*. 

 <pre><code class="react"> 
 //naimportujeme si hook knihovny 
 import {useIsAuthenticated} from "react-auth-kit"; 

 import React, {useEffect, useState} from 'react'; 
 import Nav from 'react-bootstrap/Nav'; 
 import Navbar from 'react-bootstrap/Navbar'; 
 import Container from 'react-bootstrap/Container'; 

 //Komponenta reprezentuji navigacni menu pro uzivatele 
 const NavBar = () => { 
     //interni stav komponenty, ktera si drzi informace o tom, zda je uzivatel prihlasen 
     const [isAuthenticated,setAuthenticated] = useState(false); 

     //inicializujeme hook knihovny 
     const authenticated = useIsAuthenticated(); 
     //useEffect hook komponenty 
     useEffect(()=>{ 
         //pri kazde zmene stavu komponenty se dotazeme, zda je uzivatel autentikovan 
         //token totiz uz mohl vyprset nebo samozrejme vubec nemusi existovat (uzivatel se jeste ani neprihlasil) 
         //knihovna zde neudela nic vic, nez ze se podiva, jestli existuje cookie s nazvem "token", pokud existuje 
         //tak se podiva, zda token neni expirovany (opet ma ulozeno v cookie, pouze pod jinym klicem) 
         const isAuthed = authenticated(); 
         //nastavime true/false podle toho, zda je uzivatel autentikovan, tj. ma platny a neexpirovany token 
         setAuthenticated(isAuthed); 
     }) 
     return ( 
         <Navbar bg="light" expand="lg"> 
             <Container fluid> 
                 <Navbar.Brand href="/">React-Bootstrap</Navbar.Brand> 
                 <Navbar.Toggle aria-controls="basic-navbar-nav"/> 
                 <Navbar.Collapse id="basic-navbar-nav"> 
                     <Nav className="me-auto"> 
                         <Nav.Link href="/about">About</Nav.Link> 
                         <Nav.Link href="/detect">Detect</Nav.Link> 
                         <Nav.Link href="/configuration">Configuration</Nav.Link> 
                     </Nav> 
                     { 
                         //Je uzivatel prihlasen?  
                         //pokud je prihlasen, vykresli mu tlacitkou na odlhaseni 
                         //pokud neni prihlasen, vykresli mu tlacitko na prilaseni 
                         isAuthenticated? 
                             <Nav.Link href="/logout">Logout</Nav.Link> 
                             : 
                             <Nav.Link href="/login">Sign in</Nav.Link> 
                     } 
                 </Navbar.Collapse> 
             </Container> 
         </Navbar> 
     ); 
 }; 

 export default NavBar; 

 </code></pre> 


 h3. Odhlášení uživatele 

 Tato ukázka představí poslední hook: *useSignOut*. 

 <pre><code class="react"> 
 import React, { useEffect } from 'react'; 


 //Naimportujeme si hooky. Nove tedy hook useSignOut, ktery vycisti cookies pri odhlaseni uzivatele 
 import {useSignOut,useIsAuthenticated} from "react-auth-kit"; 
 import {logoutUser} from "../api/APILogout"; 
 import {retrieveUsernameFromStorage,invalidateLocalStorage} from "../context/LocalStorageManager"; 
 function Logout() { 
     //opet inicializujeme funkce knihovny 
     //tedy funkce, ktera se pta, zda je uzivatel autentikovan 
     const isAuthenticated = useIsAuthenticated(); 
     // a funkce, ktera odhlasi uzivatele 
     const signOut = useSignOut(); 

     useEffect(() => { 
         //vytahneme z local storage jmeno uzivatele, ktery se chce odhlasit 
         const userName: string = retrieveUsernameFromStorage(); 
         //pokud je uzivatel autentikovany a zaroven znam jeho jmeno, tak jej odhlasim 
         if(isAuthenticated() && userName != null){ 
             //zavolej server s requestem na odhlaseni, tedy znevalidneni tokenu 
             logoutUser(userName); 
             //smaz celou local storage uzivatele  
             invalidateLocalStorage(); 
             //samotne volani hooku knihovny - smaze vsechny cookies, ve kterych byl ulozen token a informace k tokenu 
             signOut(); 
         } 
         //presmerujeme uzivatele zpatky na root stranku aplikace 
         window.location.href = '/'; 

     }); 
     //vratime null, aby react router pochopil, ze se proklikem v navbaru vola tato funkce a nebude se vykreslovat komponenta 
     return null; 
 } 

 export default Logout; 

 </code></pre>