Projekt

Obecné

Profil

React autentizace » Historie » Verze 16

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 16 Jiří Trefil
* Následují ukázky kódu. které: přihlásí uživatele, zkontrolují, zda je uživatel autentikován a odhlásí 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>