#API – Konfiguracja JSON Web Token w Symfony

Kiedy szukałem informacji, o autoryzacji użytkowników w tego typu aplikacjach, natknąłem się na JSON Web Token. JWT, to otwarty standard generowania tokenów, dzięki którym można łatwo weryfikować użytkowników w naszej aplikacji. Generowane tokeny mają dość kompaktowy rozmiar, więc można je bez problemu wysyłać metodami POST, GET, czy w nagłówkach HTTP.

Więcej o JWT można znaleźć w oficjalnym wprowadzeniu do JWT.

Wykorzystam w swoim projekcie pakiet LexikJWTAuthenticationBundle. Aby dodać go do projektu, muszę użyć polecenia composera:

$ php composer require "lexik/jwt-authentication-bundle"

a następnie dodać go do pakietów projektu symfony, czyli do pliku AppKrenel.php:

public function registerBundles()
{
    return array(
        // ...
        new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
    );
}

Muszę teraz wygenerować klucze SSL, wymagane do działania pakietu z kodowaniem RS256. Aby to zrobić muszę upewnić się, że mam zainstalowany pakiet OpenSSL w moim systemie. W linux Fedora, z którego korzystam, instalacja pakietów wygląda tak:

$ sudo dnf install openssl

A następnie tworzę klucze:

$ mkdir -p var/jwt
$ openssl genrsa -out var/jwt/private.pem -aes256 4096
$ openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem

jako hasło przy tworzeniu kluczy, wpisałem „warsztat”.

Teraz muszę dodać następujące opcje do konfiguracji w pliku config.yml:

lexik_jwt_authentication:
    private_key_path: '%jwt_private_key_path%'
    public_key_path:  '%jwt_public_key_path%'
    pass_phrase:      '%jwt_key_pass_phrase%'
    token_ttl:        '%jwt_token_ttl%'

Natomiast w plikach parameters.yml, oraz parameters.yml.dist:

jwt_private_key_path: '%kernel.root_dir%/../var/jwt/private.pem'
jwt_public_key_path:  '%kernel.root_dir%/../var/jwt/public.pem'
jwt_key_pass_phrase:  'warsztat' # hasło wpisane podczas tworzenia kluczy
jwt_token_ttl:  3600

Teraz pora na edycję pliku security.yml, gdzie znajduje się cała konfiguracja zabezpieczeń naszej aplikacji. Najpierw zajmę się sekcją providers. Odpowiada ona za konfiguracje usługi udostępniającej hasła i loginy użytkowników. Nie mam jeszcze stworzonej tabeli z użytkownikami w bazie danych, więc wpiszę tutaj użytkowników na sztywno, póki co, bez żadnego kodowania, czyli plaintext:

security:
    #...
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    providers:
        in_memory:
            memory:
                users:
                    user:
                        password: password
                        roles: 'ROLE_USER'
                    admin:
                        password: password2
                        roles: 'ROLE_ADMIN'

Całą sekcję firewall, podmieniam na tą z dokumentacji LexikJWTAuthenticationBundle. Na razie to tylko testy, więc nie przejmuję się ich zawartością, zajmę się tym później:

    firewalls:

        login:
            pattern:  ^/api/login
            stateless: true
            anonymous: true
            form_login:
                check_path:               /api/login_check
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        api:
            pattern:   ^/api
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator

    access_control:
        - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

Teraz do pliku routing.yml dodaję nowe przekierowanie dla usługi sprawdzającej dane użytkownika:

api_login_check:
    path: /api/login_check

Pora sprawdzić, czy generowanie tokenów działa. Są na to dwa sposoby. Pierwszy, to użyć komendy curl i przy jej pomocy wysłać dane, lub skorzystać z jakiegoś narzędzia, jak np. Postman. Ja wybrałem drugą opcję, ponieważ wg. mnie jest wygodniejsza. Postman, to aplikacja webowa, dostępna w sklepie chrome. Wystarczy ją stamtąd dodać do przeglądarki i już można korzystać.

W pierwszej kolejności wpisuję adres serwera, z przekierowaniem do login_check:

http://127.0.0.1:8000/api/login_check

Później przechodzę do zakładki body, opcja form-data i dodaję dwa nowe pola _username, oraz _password. Wpisuję w nich nazwę użytkownika i hasło podane w security.yml, a następnie klikam Send. Otrzymuję token JWT, czyli wszystko zostało skonfigurowane poprawnie:

zrzut-ekranu-z-2017-03-05-21-10-57

Generowanie tokenów działa, teraz pora sprawdzić, czy token jest prawidłowy i czy będę mógł przy jego pomocy dokonać autoryzacji.

Do kontrolera DefaultController.php, dodaję nową metodę apiPagesAction, z przekierowaniem /api/pages:

<?php 
//... 
use Symfony\Component\HttpFoundation\JsonResponse; 

class DefaultController extends Controller 
{     
    //...   
  
    /**      
    * @Route("/api/pages", name="api_pages")      
    */     
    public function apiPagesAction(Request $request)     
    {         
        $data = [             
            "data" => "JWT Token is valid"
        ];
        $response = new JsonResponse();
        return $response->setData($data);
    }
}

Metoda ta, zwraca nam w formacie JSON, informację, że token jest poprawny, jest to zwyczajna tablica na sztywno przypisana do zmiennej i zwrócona jako obiekt JsonResponse. Należy pamiętać, o dodaniu przestrzeni nazw JsonResponse. W konfiguracji security.yml w access_control, mam zabezpieczone wszystkie przekierowania do /api, poza /api/login, więc jeśli w przeglądarce wpiszę:

http://127.0.0.1:8000/api/pages

Otrzymam błąd o braku tokena:

{
    "code": 401,
    "message": "JWT Token not found"
}

Żeby dostać się do tej strony, muszę przesłać nagłówek z tokenem, w tym celu korzystam z Postmana. Jako adres wpisuję:

http://127.0.0.1:8000/api/pages

I przechodzę do zakładki headers, następnie dodaję nowy element nagłówka o nazwie  Authorization, a jako jego parametr wygenerowany wcześniej token, poprzedzony słowem „Bearer” i klikam Send:

zrzut-ekranu-z-2017-03-05-21-38-41

Otrzymałem dane o poprawnym tokenie, czyli wszystko działa poprawnie.

Jako następne, biorę na warsztat testy jednostkowe. Mam nadzieję, że uda mi się je ogarnąć w miarę szybko, a na razie to tyle.

Commit

Pozdrawiam!
MTK

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

Up ↑