React autentizace » Historie » Verze 15
Jiří Trefil, 2023-04-23 10:17
1 | 2 | Jiří Trefil | h1. Autentizace na straně klienta (React aplikace) |
---|---|---|---|
2 | |||
3 | h2. Popis logiky autentizace |
||
4 | |||
5 | * Uživatel se přihlásí pod svým účtem. |
||
6 | |||
7 | * 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). |
||
8 | 3 | Jiří Trefil | |
9 | * 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. |
||
10 | 4 | Jiří Trefil | !clipboard-202304231139-vx5qh.png! |
11 | 3 | Jiří Trefil | |
12 | 5 | Jiří Trefil | * Pouze takto je uživatel považován za autentikováno a může se svobodně pohybovat po aplikaci bez omezení. |
13 | |||
14 | |||
15 | |||
16 | h1. Knihovna *react-auth-kit* |
||
17 | 6 | Jiří Trefil | |
18 | 9 | Jiří Trefil | * K implementaci této feature byla použita knihovna třetí strany. Knihovna spravuje celý stav autentizace na straně klienta. |
19 | * 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í. |
||
20 | * Následuje ukázka kódu, která používá knihovnu pro přihlášení uživatele. |
||
21 | 8 | Jiří Trefil | |
22 | 12 | Jiří Trefil | h3. Přihlášení uživatele |
23 | 11 | Jiří Trefil | |
24 | 13 | Jiří Trefil | Ukázka kódu představuje první hook: *UseSignIn*. |
25 | |||
26 | 8 | Jiří Trefil | <pre><code class="react"> |
27 | //importujeme useEffect knihovny pro prihlaseni uzivatele |
||
28 | import {useSignIn} from "react-auth-kit"; |
||
29 | |||
30 | //komponenta pro prihlaseni uzivatele |
||
31 | const LoginComponent = () => { |
||
32 | //initneme funkci knihovny |
||
33 | //ktera udela vsechnu praci s ukladanim tokenu |
||
34 | const signIn = useSignIn(); |
||
35 | |||
36 | //dummy funkce, ktera pouze demonstruje, jak se knihovna pouziva |
||
37 | //samotna implementace v kodu je v jadru stejna, pouze je kolem vice omacky |
||
38 | //pro tuto ukazku nedulezite |
||
39 | const sampleLogin = async (username:string, password: string) =>{ |
||
40 | //V tomto bode zavolame server s nasim login pozadavkem |
||
41 | //odpoved serveru je ve formatu jako nasledujici rozepsany objekt |
||
42 | //tedy vratime nam status (HTTP code) - tim zjistime, jestli se neco nepokazilo |
||
43 | //zpravu (message) - pro blizsi informace o vysledku informace |
||
44 | //samotny jwt token (token) - timto tokenem se uzivatel autentikuje s kazdym nasledujicim pozadavkem na server |
||
45 | const responseWrapper: {message:string; status:number; token:string} = await login(username,password); |
||
46 | //token muze byt empty string, pokud doslo k chybe prihlaseni |
||
47 | const accessToken:string = responseWrapper.token; |
||
48 | if(responseWrapper.status != 200){ |
||
49 | //Zde by byla cast, ktera by vypsala uzivateli chybovou hlasku, protoze se prihlaseni nepovedlo |
||
50 | |||
51 | } |
||
52 | //volame samotny useEffect pro ulozeni tokenu do cookie |
||
53 | signIn({ |
||
54 | //token, ktery ukladame, v nasem pripade promenna accessToken |
||
55 | // cookie bude mit nazev "token" |
||
56 | token:accessToken, |
||
57 | //jak dlouho token zije (v minutach) |
||
58 | expiresIn: 3600, |
||
59 | //typ tokenu, v pripade JWT se jedna o Bearer token |
||
60 | tokenType: "Bearer", |
||
61 | // co dalsiho si chceme ulozit, napriklad jmeno uzivatele, nutne to tu byt nemusi |
||
62 | authState: {userName:username} |
||
63 | }); |
||
64 | |||
65 | |||
66 | } |
||
67 | |||
68 | |||
69 | } |
||
70 | export default LoginComponent; |
||
71 | </code></pre> |
||
72 | |||
73 | 14 | Jiří Trefil | h3. Kontrola, zda je uživatel autentikován |
74 | 1 | Jiří Trefil | |
75 | 14 | Jiří Trefil | Tato ukázka kódu představuje druhý hook: *useIsAuthenticated*. |
76 | |||
77 | <pre><code class="react"> |
||
78 | //naimportujeme si hook knihovny |
||
79 | import {useIsAuthenticated} from "react-auth-kit"; |
||
80 | |||
81 | import React, {useEffect, useState} from 'react'; |
||
82 | import Nav from 'react-bootstrap/Nav'; |
||
83 | import Navbar from 'react-bootstrap/Navbar'; |
||
84 | import Container from 'react-bootstrap/Container'; |
||
85 | |||
86 | //Komponenta reprezentuji navigacni menu pro uzivatele |
||
87 | const NavBar = () => { |
||
88 | //interni stav komponenty, ktera si drzi informace o tom, zda je uzivatel prihlasen |
||
89 | const [isAuthenticated,setAuthenticated] = useState(false); |
||
90 | |||
91 | //inicializujeme hook knihovny |
||
92 | const authenticated = useIsAuthenticated(); |
||
93 | //useEffect hook komponenty |
||
94 | useEffect(()=>{ |
||
95 | //pri kazde zmene stavu komponenty se dotazeme, zda je uzivatel autentikovan |
||
96 | //token totiz uz mohl vyprset nebo samozrejme vubec nemusi existovat (uzivatel se jeste ani neprihlasil) |
||
97 | //knihovna zde neudela nic vic, nez ze se podiva, jestli existuje cookie s nazvem "token", pokud existuje |
||
98 | //tak se podiva, zda token neni expirovany (opet ma ulozeno v cookie, pouze pod jinym klicem) |
||
99 | const isAuthed = authenticated(); |
||
100 | //nastavime true/false podle toho, zda je uzivatel autentikovan, tj. ma platny a neexpirovany token |
||
101 | setAuthenticated(isAuthed); |
||
102 | }) |
||
103 | return ( |
||
104 | <Navbar bg="light" expand="lg"> |
||
105 | <Container fluid> |
||
106 | <Navbar.Brand href="/">React-Bootstrap</Navbar.Brand> |
||
107 | <Navbar.Toggle aria-controls="basic-navbar-nav"/> |
||
108 | <Navbar.Collapse id="basic-navbar-nav"> |
||
109 | <Nav className="me-auto"> |
||
110 | <Nav.Link href="/about">About</Nav.Link> |
||
111 | <Nav.Link href="/detect">Detect</Nav.Link> |
||
112 | <Nav.Link href="/configuration">Configuration</Nav.Link> |
||
113 | </Nav> |
||
114 | { |
||
115 | //Je uzivatel prihlasen? |
||
116 | //pokud je prihlasen, vykresli mu tlacitkou na odlhaseni |
||
117 | //pokud neni prihlasen, vykresli mu tlacitko na prilaseni |
||
118 | isAuthenticated? |
||
119 | <Nav.Link href="/logout">Logout</Nav.Link> |
||
120 | : |
||
121 | <Nav.Link href="/login">Sign in</Nav.Link> |
||
122 | } |
||
123 | </Navbar.Collapse> |
||
124 | </Container> |
||
125 | </Navbar> |
||
126 | ); |
||
127 | }; |
||
128 | |||
129 | export default NavBar; |
||
130 | |||
131 | </code></pre> |
||
132 | 15 | Jiří Trefil | |
133 | |||
134 | h3. Odhlášení uživatele |
||
135 | |||
136 | Tato ukázka představí poslední hook: *useSignOut*. |
||
137 | |||
138 | <pre><code class="react"> |
||
139 | import React, { useEffect } from 'react'; |
||
140 | |||
141 | |||
142 | //Naimportujeme si hooky. Nove tedy hook useSignOut, ktery vycisti cookies pri odhlaseni uzivatele |
||
143 | import {useSignOut,useIsAuthenticated} from "react-auth-kit"; |
||
144 | import {logoutUser} from "../api/APILogout"; |
||
145 | import {retrieveUsernameFromStorage,invalidateLocalStorage} from "../context/LocalStorageManager"; |
||
146 | function Logout() { |
||
147 | //opet inicializujeme funkce knihovny |
||
148 | //tedy funkce, ktera se pta, zda je uzivatel autentikovan |
||
149 | const isAuthenticated = useIsAuthenticated(); |
||
150 | // a funkce, ktera odhlasi uzivatele |
||
151 | const signOut = useSignOut(); |
||
152 | |||
153 | useEffect(() => { |
||
154 | //vytahneme z local storage jmeno uzivatele, ktery se chce odhlasit |
||
155 | const userName: string = retrieveUsernameFromStorage(); |
||
156 | //pokud je uzivatel autentikovany a zaroven znam jeho jmeno, tak jej odhlasim |
||
157 | if(isAuthenticated() && userName != null){ |
||
158 | //zavolej server s requestem na odhlaseni, tedy znevalidneni tokenu |
||
159 | logoutUser(userName); |
||
160 | //smaz celou local storage uzivatele |
||
161 | invalidateLocalStorage(); |
||
162 | //samotne volani hooku knihovny - smaze vsechny cookies, ve kterych byl ulozen token a informace k tokenu |
||
163 | signOut(); |
||
164 | } |
||
165 | //presmerujeme uzivatele zpatky na root stranku aplikace |
||
166 | window.location.href = '/'; |
||
167 | |||
168 | }); |
||
169 | //vratime null, aby react router pochopil, ze se proklikem v navbaru vola tato funkce a nebude se vykreslovat komponenta |
||
170 | return null; |
||
171 | } |
||
172 | |||
173 | export default Logout; |
||
174 | |||
175 | </code></pre> |