#Angular2 – JSON Web Token

Od pięciu dni tworzę już frontend mojej aplikacji i tak jak myślałem, będę musiał robić poprawki w API. Zapomniałem np., że przy wyświetlaniu wszystkich dokumentów chciałem wyświetlić nazwę klienta. Będę musiał to dodać, ale na razie, skupiłem się na tym, aby zaimplementować logowanie. Nie będę wklejał tutaj całego kodu, ponieważ dodam tutaj link do tutoriala, z którego korzystałem. Wprowadziłem jedynie kilka zmian, które postaram się tutaj omówić.

„Angular 2 JWT Authentication Example & Tutorial” z tego poradnika właśnie korzystałem. Wprowadziłem jedynie zmiany w pliku authentication.service.ts.

import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map'
 
@Injectable()
export class AuthenticationService {
    public token: string;
 
    constructor(private http: Http) {

        var currentUser = JSON.parse(localStorage.getItem('currentUser'));
        this.token = currentUser && currentUser.token;
    }
 
    login(username: string, password: string): Observable {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        return this.http.post('http://localhost:8000/api/login_check', JSON.stringify({ _username: username, _password: password }), options)
            .map(response => {
                if(response.status < 200 || response.status >= 300) {
                    return false;
                }else{
                    this.token = response.json() && response.json().token;
                    localStorage.setItem('currentUser', JSON.stringify({ username: username, token: this.token }));
                    return true;
                }
            })
            .catch(() => Observable.of(false));
    }
 
    logout(): void {
        // clear token remove user from local storage to log user out
        this.token = null;
        localStorage.removeItem('currentUser');
    }
    
}

Tak wygląda plik po moich zmianach. Pierwszym problemem, był brak nagłówka informującego API, że wysyłane dane, są w formacie JSON, o czym pisałem w ostatnim poście. Wystarczy dodać dwa obiekty, jeden klasy Headers, drugi RequestOptions i ten drugi dodać do metody post obiektu http:

//...
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post('http://localhost:8000/api/login_check', JSON.stringify({ _username: username, _password: password }), options)
//...

W założeniach z tutorialu, jeśli API nie zautoryzuje użytkownika, to zwróci pusty token. W moim przypadku API nie zwraca pustego tokena, a kod błędu 401. Przez to usługa logowania zwracała błąd, a działanie skryptu było przerywane. Musiałem wyłączyć przechwytywanie błędów i dodać funkcję warunkową sprawdzającą kod błędu:

//...
.map(response => {
    if(response.status < 200 || response.status >= 300) {
        return false; //jeśli kod błędu nie mieści sie w przedziale 200-299, to zwróć false
    }else{
        this.token = response.json() && response.json().token;
        localStorage.setItem('currentUser', JSON.stringify({ username: username, token: this.token }));
        return true;
    }
})
.catch(() => Observable.of(false));
//...

Resztę wykonałem według tutorialu.

Kolejnym problemem, jaki napotkałem, była zmiana menu, w zależności od tego, czy użytkownik jest zalogowany, czy nie. Token oraz nazwa użytkownika, jest zapisywana w localStorage(). Żeby w menu bocznym zmieniać nazwę użytkownika, w jego komponencie przypisałem zmienne z localStorage do zmiennych username i firstLetter (pobiera pierwszą literę z nazwy, do wyświetlenia w „avatarze”). W template sprawdzałem jedynie, czy te zmienne są ustawione, czy nie. Przez to, po wylogowaniu, czy zalogowaniu, trzeba było przeładować całą stronę, żeby zaktualizować boczne menu.

Chwilę zajęło mi znalezienie rozwiązania, ale jest ono banalnie proste. Wystarczy umieścić przypisywanie do zmiennych w metodzie:

export class SidebarComponent implements OnInit {
    username = '';
    firstLetter = '';

    constructor() {}

    ngOnInit() {} 

    public isLoggedIn() {
        let userData = JSON.parse(localStorage.getItem("currentUser"));
        if(userData) {
            this.username = userData.username;
            this.firstLetter = userData.username.substring(0,1);
            return true;
        }else{
            return false;
        }
    }
}

Teraz w template, wystarczy sprawdzać, jaką wartość przyjmuje metoda isLoggedIn():

<div *ngIf="isLoggedIn()">
    <!--zalogowany-->
<div>
<div *ngIf="!isLoggedIn()">
    <!--niezalogowany-->
<div>

Poza tym wszystkim dodałem routing i pobieranie dokumentów, w widoku documents. W ten weekend mam zamiar napisać usługę breadcrumbs, a na przyszły tydzień przewiduję ukończenie componentu documents.

To tyle na dziś, oczywiście wszystkie zmiany można znaleźć na GitHub.

Pozdrawiam!

MTK

Proudly powered by WordPress | Theme: Baskerville 2 by Anders Noren.

Up ↑