JSON Web Token i jego obsługa w RestAssured

Tomasz Niegowski

JWT, czyli JSON Web Token, to kompaktowy i samowystarczalny sposób bezpiecznego przesyłania informacji między dwiema stronami. Jest często używany do uwierzytelniania i autoryzacji w aplikacjach internetowych, interfejsach API i systemach rozproszonych. Mówiąc prościej, jest niczym cyfrowy paszport lub dowód osobisty, który zawiera zakodowane informacje o użytkowniku lub encji.

Co to jest JWT – wprowadzenie

JWT składa się z trzech części: nagłówka, zawartości i podpisu. Nagłówek określa typ tokena (którym jest JWT) i algorytm użyty do jego podpisania. Zawartość to rzeczywiste dane lub stwierdzenia („claims”), które mogą zawierać informacje, takie jak identyfikator użytkownika, rola, uprawnienia i inne istotne szczegóły. Stwierdzenia te zakodowano jako obiekt JSON.

Aby zapewnić integralność i autentyczność tokena, podpis generuje się przez połączenie nagłówka, zawartości i tajnego klucza znanego tylko serwerowi. Jest on dodawany do tokena, dzięki czemu odznacza się odpornością na manipulacje. Gdy serwer odbierze token, może zweryfikować podpis przy użyciu tego samego tajnego klucza i upewnić się, że token nie został zmodyfikowany lub naruszony.

Przykład

Nagłówek („Header”):

{
  "alg": "HS256",
  "typ": "JWT"
}

Zawartość („Payload”):

{
  "name": "admin",
  "age": 35
}

Podpis („Signature”):

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
my_secret
)

Zakodowana postać tokena JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWRtaW4iLCJhZ2UiOjM1fQ.zt3sQAm2-9gjbRSh20rjn1nAPIv3vNhgQM2-31I5DRY

Jak przetestować działanie JWT za pomocą frameworka RestAssured?

Dla prostego przypadku testowego użyję strony https://demoqa.com/. Zanim zacznę, przede wszystkim muszę przygotować kilka rzeczy, aby uzyskać token JWT. Zgodnie ze swaggerem DemoQA trzeba utworzyć użytkownika, wysyłając POST do punktu końcowego /Account/v1/User z danymi użytkownika. Jak to zrobić? Spójrz:

private String body = "{\n" +
  "  \"userName\": \"" + USERNAME + "\",\n" +
  "  \"password\": \"" + PASSWORD + "\"\n" +
"}";

Tak natomiast wygląda wysłanie danych użytkownika i pobranie jego identyfikatora z pola „userID” z odpowiedzi:

private void createUser() {
  userID = given()
    .contentType("application/json")
    .body(body)
    .when()
    .post(USER_ENDPOINT)
    .jsonPath()
    .get("userID");
}


Po pomyślnym utworzeniu użytkownika nadszedł czas, aby wygenerować dla niego token. Podążając za swaggerem, muszę wysłać zapytanie typu POST z tymi samymi danymi użytkownika na endpoint /Account/v1/GenerateToken. Zobacz:

private void generateToken() {
  token = given()
    .contentType("application/json")
    .body(body)
    .when()
    .post(GENERATE_TOKEN_ENDPOINT)
    .jsonPath()
    .get("token");
}

Podobnie jak w poprzednim fragmencie kodu token został pobrany z odpowiedzi json, przyjmując wartość elementu „token”.

Gotowe? Po tym wstępie mogę wykonać dwa proste przypadki testowe, wysyłając zapytanie GET na /Account/v1/User. W pierwszym przypadku zamierzam autoryzować żądanie za pomocą już pobranego tokena, podczas gdy w drugim nie.

Zapytanie typu „GET” wraz z autoryzacją tokenem JWT:

@Test
void getAuthorizedUser() {
  given()
    .contentType("application/json")
    .headers("Authorization", "Bearer " + token)
    .when()
    .get(USER_ENDPOINT + "/" + userID)
    .then()
    .assertThat()
    .statusCode(HttpStatus.SC_OK);
}

Zapytanie typu „GET” pozbawione tokena JWT:

@Test
void getUnauthorizedUser() {
  given()
    .contentType("application/json")
    .when()
    .get(USER_ENDPOINT + "/" + userID)
    .then()
    .assertThat()
    .statusCode(HttpStatus.SC_UNAUTHORIZED);
}

Powyższe przypadki testowe są bardzo podobne. Jedyną różnicą jest dodanie nagłówka Authorization wraz z tokenem w jednym z żądań. Schemat Bearer jest powszechną konwencją podczas obsługi tokena JWT w nagłówku autoryzacji. Spójrz:

Authorization: Bearer /token/

Jak widzisz, odpowiedź na żądanie autoryzowanego użytkownika ma kod odpowiedzi HTTP 200. Oznacza to, że cała operacja zakończyła się pomyślnie. W drugim przypadku, gdy użytkownik jest nieautoryzowany (brak tokena w nagłówku), kod statusu odpowiedzi to 401, a więc „nieautoryzowany”.

Podsumowanie

Artykuł zawiera informacje o obsłudze tokena JSON Web Token (JWT) przy użyciu biblioteki Rest Assured. Wyjaśniłem w nim znaczenie JWT w zabezpieczaniu aplikacji internetowych i interfejsów API. Główna część artykułu to przewodnik krok po kroku dotyczący procesu implementacji uwierzytelniania JWT w Rest Assured. Obejmuje on podstawowe pojęcia, takie jak uzyskiwanie i analizowanie JWT, ustawianie nagłówków i obsługa autoryzowanych żądań. Po zapoznaniu się z nim uzyskasz podstawową wiedzę i narzędzia do pomyślnego wdrożenia i walidacji uwierzytelniania z wykorzystaniem JWT w scenariuszach testowych Rest Assured, zachowując dobre praktyki testowania API.

Szukasz informacji o migracjach baz danych? Przeczytaj nasz artykuł: Flyway – prosta migracja bazy danych w Spring

Bibliografia

 

FAQ

Czym jest JSON Web Token (JWT)?

JSON Web Token (JWT) to kompaktowy i samowystarczalny standard przesyłania informacji jako obiekt JSON, który jest bezpieczny i odporny na manipulacje. Służy głównie do uwierzytelniania i autoryzacji w aplikacjach internetowych oraz API.

Z jakich części składa się token JWT?

Token JWT składa się z trzech części oddzielonych kropkami: nagłówka (header), zawartości (payload) oraz podpisu (signature). Nagłówek określa typ i algorytm, zawartość przechowuje dane (tzw. „claims”), a podpis weryfikuje integralność tokena.

Jak podpis JWT zapewnia bezpieczeństwo?

Podpis jest tworzony przez połączenie zakodowanego nagłówka, zakodowanej zawartości oraz tajnego klucza znanego tylko serwerowi. Pozwala to serwerowi zweryfikować, czy token jest autentyczny i czy jego zawartość nie została zmodyfikowana.

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami
kobieta pracuje na macbooku pracownicy j labs dwóch mężczyzn i kobieta w biurze