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.

Wprowadzenie do JWT

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.

Bibliografia

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

Skontaktuj się z nami