Powrót do strony głównej

Jak wdrożyć uwierzytelnianie JWT: przewodnik krok po kroku

Ten kompleksowy przewodnik wyjaśnia, jak wdrożyć uwierzytelnianie JWT dla nowoczesnych interfejsów API. Obejmuje architekturę tokenów, implementację krok po kroku z przykładami w .NET i Node.js, najlepsze praktyki bezpieczeństwa, w tym rotację tokenów odświeżania i podpisywanie asymetryczne, oraz typowe pułapki, których należy unikać.

Wdrożenie uwierzytelniania JWT: najlepsze praktyki bezpieczeństwa
Advertisement 728x90

.NET

JWT-autentykacja: kompletny przewodnik po implementacji

Implementacja bezpiecznej autentykacji to kluczowe zadanie dla nowoczesnych web API i aplikacji. JSON Web Tokens (JWT) stały się standardem branżowym dla autentykacji bezstanowej (stateless), umożliwiając serwerom weryfikację tożsamości użytkownika bez przechowywania stanu sesji po stronie serwera. Pod koniec tego przewodnika zrozumiesz pełną architekturę autentykacji JWT, będziesz w stanie zaimplementować bezpieczne wydawanie i weryfikację tokenów w swoich aplikacjach, a także otrzymasz gotową do produkcji strategię zarządzania cyklem życia tokenów. Zdobędziesz praktyczną wiedzę do bezpiecznej implementacji autentykacji JWT w dowolnym stosie technologicznym.

Czego się nauczysz

Zrozumiesz, jak architektonicznie działają JWT, dlaczego poprawna implementacja jest ważna dla bezpieczeństwa, oraz zdobędziesz praktyczne umiejętności konfiguracji middleware autentykacji, wydawania tokenów i ochrony punktów końcowych API. W efekcie będziesz gotów zaimplementować autentykację JWT w swoich projektach z pewnością co do podejmowanych decyzji dotyczących bezpieczeństwa.

Google AdInline article slot

Zrozumienie architektury autentykacji JWT

Zanim przejdziesz do kodu, ważne jest, aby zrozumieć, jak działa autentykacja JWT. JSON Web Token to kompaktowy, bezpieczny dla URL sposób reprezentowania oświadczeń (claims) między dwiema stronami. Token składa się z trzech części zakodowanych w Base64URL i oddzielonych kropkami: nagłówka, ładunku (payload) i podpisu (header.payload.signature).

Przepływ autentykacji JWT podąża za jasnym wzorcem: klient uwierzytelnia się (zazwyczaj przez punkt końcowy logowania), otrzymuje podpisany token, a następnie dołącza ten token do nagłówka Authorization w kolejnych żądaniach. Serwer weryfikuje podpis i oświadczenia tokena przy każdym żądaniu, co czyni system bezstanowym (stateless) — nie wymaga przechowywania stanu sesji po stronie serwera.

Kluczowe komponenty JWT

Komponent Przeznaczenie Przykład
Nagłówek Określa algorytm podpisu i typ tokena {"alg":"RS256","typ":"JWT"}
Ładunek (Payload) Zawiera oświadczenia (dane użytkownika, czas wygaśnięcia, emitent) {"sub":"user123","exp":1700000000}
Podpis Zapewnia integralność i autentyczność tokena Kryptograficzny skrót nagłówka i ładunku z sekretem

Taka konstrukcja czyni JWT idealnymi dla systemów rozproszonych i mikrousług, gdzie utrzymywanie wspólnego stanu sesji byłoby niepraktyczne.

Google AdInline article slot

Jak zaimplementować autentykację JWT: przewodnik krok po kroku

Poniższe kroki opisują gotowe do produkcji podejście do implementacji autentykacji JWT. Chociaż przykłady używają .NET i Node.js, zasady mają zastosowanie do wszystkich języków programowania.

Krok 1: Zainstaluj niezbędne pakiety autentykacji

Nowoczesne frameworki dostarczają oficjalne handlerów autentykacji JWT Bearer. Dla .NET zainstaluj pakiet Microsoft.AspNetCore.Authentication.JwtBearer. Dla Node.js użyj jsonwebtoken.

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

# Node.js
npm install jsonwebtoken bcryptjs dotenv

Korzystanie z oficjalnych, sprawdzonych bibliotek jest kluczowe — nigdy nie implementuj własnej logiki kryptograficznej podpisu.

Google AdInline article slot

Krok 2: Skonfiguruj usługi autentykacji

Konfiguracja autentykacji określa, jak Twoje API weryfikuje przychodzące tokeny. Krytyczne parametry weryfikacji obejmują:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,           // Sprawdź, czy token pochodzi od znanego emitenta
            ValidateAudience = true,         // Sprawdź, czy token jest przeznaczony dla tego API
            ValidateLifetime = true,         // Automatycznie odrzucaj wygasłe tokeny
            ValidateIssuerSigningKey = true, // Sprawdź kryptograficzny podpis
            ValidIssuer = "https://twoj-dostawca-tozsamosci.com",
            ValidAudience = "https://twoje-api.com",
            IssuerSigningKey = publicKey     // Klucz do weryfikacji podpisu
        };
    });

Wszystkie cztery flagi weryfikacji powinny być ustawione na true w środowisku produkcyjnym. Wyłączenie któregokolwiek z nich tworzy luki w zabezpieczeniach. IssuerSigningKey może być załadowany z pliku PEM lub, w produkcji, automatycznie pobrany z punktu końcowego metadanych dostawcy tożsamości.

Krok 3: Dodaj middleware autentykacji i autoryzacji

Kolejność komponentów middleware jest krytyczna — autentykacja musi być wykonana przed autoryzacją:

app.UseAuthentication();  // Sprawdza JWT i wypełnia HttpContext.User
app.UseAuthorization();   // Stosuje polityki [Authorize] na chronionych punktach końcowych

Gdy żądanie dociera do UseAuthentication, handler sprawdza nagłówek Authorization, weryfikuje JWT i tworzy ClaimsPrincipal reprezentujący uwierzytelnionego użytkownika. Ten obiekt jest następnie dostępny w całym potoku żądania do podejmowania decyzji autoryzacyjnych.

Krok 4: Zabezpiecz punkty końcowe za pomocą atrybutu [Authorize]

Do ochrony punktów końcowych API wymagany jest atrybut [Authorize]:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return Ok($"Uwierzytelniony użytkownik: {userId}");
    }
}

Żądania bez ważnego tokena otrzymują odpowiedź 401 Unauthorized. Gdy autentykacja jest pomyślna, ale brakuje wymaganych uprawnień, API zwraca 403 Forbidden.

Krok 5: Zaimplementuj wydawanie tokenów (tylko do celów deweloperskich)

W środowisku produkcyjnym wydawanie tokenów należy do dedykowanego dostawcy tożsamości (IdP), implementującego OAuth 2.0 lub OpenID Connect. Rola API ogranicza się do weryfikacji.

Jednak do celów deweloperskich i testowych może być potrzebny lokalny punkt końcowy dla tokenów:

// Przykład w Node.js z jsonwebtoken
router.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ message: 'Nieprawidłowe dane logowania' });
  }
  
  const token = jwt.sign(
    { id: user._id, email: user.email },
    process.env.JWT_SECRET,
    { expiresIn: '15m' } // Krótko żyjące tokeny dostępu
  );
  
  res.json({ token });
});

Tokeny dostępu powinny mieć krótki czas życia — zazwyczaj 15 minut — aby ograniczyć szkody w przypadku kradzieży tokena.

Najlepsze praktyki bezpieczeństwa dla środowiska produkcyjnego

Nigdy nie przechowuj tokenów w LocalStorage

W aplikacjach webowych przechowuj JWT w ciasteczkach HttpOnly, Secure, a nie w localStorage lub sessionStorage. Zapobiega to dostępowi do tokenów poprzez skrypty między witrynami (XSS). Konfiguracja ciasteczka powinna obejmować:

HttpOnly: true       // Zapobiega dostępowi z JavaScript
Secure: true         // Wysyłane tylko przez HTTPS
SameSite: Lax lub Strict  // Ochrona przed CSRF

Używaj rotacji tokenów odświeżania (refresh tokens)

Ponieważ tokeny dostępu szybko wygasają, zaimplementuj mechanizm tokenów odświeżania, aby utrzymać sesje użytkowników bez ponownej autentykacji:

  • Token odświeżania: Długo żyjący (np. 7 dni), bezpiecznie przechowywany na serwerze
  • Token dostępu: Krótko żyjący (np. 15 minut), wysyłany z każdym żądaniem
  • Rotacja: Każde żądanie odświeżenia wydaje nową parę tokenów, unieważniając stary token odświeżania

To podejście łagodzi skutki kradzieży tokenów i zapewnia czystą funkcjonalność wylogowania.

Używaj podpisu asymetrycznego (RS256/ES256) dla środowiska produkcyjnego

Używaj algorytmów asymetrycznych (RS256 lub ES256) zamiast HMAC (HS256) w produkcji:

Algorytm Typ klucza Scenariusz użycia
HS256 Symetryczny (wspólny sekret) Pojedyncze aplikacje
RS256/ES256 Asymetryczny (klucz publiczny/prywatny) Systemy wielousługowe, mikrousługi

Podpis asymetryczny pozwala wielu usługom weryfikować tokeny za pomocą klucza publicznego, podczas gdy tylko emitent posiada klucz prywatny — zgodnie z zasadą najmniejszych uprawnień.

Sprawdzaj wszystkie standardowe oświadczenia

Następujące oświadczenia są obowiązkowe dla tokenów dostępu OAuth 2.0 i powinny być weryfikowane:

  • iss (Emitent) — zgodny z oczekiwanym emitentem
  • aud (Audytorium) — zgodny z identyfikatorem Twojego API
  • exp (Czas wygaśnięcia) — token nie wygasł
  • sub (Podmiot) — identyfikator użytkownika
  • iat (Czas wydania) — token został wydany w przeszłości
  • jti (ID JWT) — unikalny identyfikator do śledzenia tokena

Autentykacja bezstanowa z Redis

Chociaż JWT są z natury bezstanowe, implementacja unieważniania tokenów wymaga warstwy przechowywania. Redis dostarcza idealne rozwiązanie:

// Przechowywanie metadanych tokena do unieważnienia
func (r *Redis) SetJTI(ctx context.Context, key, userID string, exp time.Time) error {
    return r.Client.Set(ctx, key, userID, time.Until(exp)).Err()
}

// Sprawdzenie, czy token został unieważniony
func (r *Redis) GetUserByJTI(ctx context.Context, key string) (string, error) {
    return r.Client.Get(ctx, key).Result()
}

Przechowuj unikalny identyfikator tokena (jti) w Redis z TTL odpowiadającym czasowi wygaśnięcia tokena. Zapewnia to natychmiastowe wylogowanie i zapobiega ponownemu użyciu skompromitowanych tokenów.

Częste błędy w implementacji JWT, których należy unikać

  1. Samodzielne wydawanie tokenów w produkcji: API powinny weryfikować tokeny, a nie je wydawać. Używaj dedykowanego IdP do wydawania.

  2. Słabe klucze podpisu: Używaj kryptograficznie silnych sekretów (co najmniej 32 bajty), wygenerowanych przez openssl rand -base64 32.

  3. Wyłączanie weryfikacji oświadczeń: Nigdy nie ustawiaj ValidateIssuer lub ValidateAudience na false w produkcji.

  4. Długi czas życia tokenów: Tokeny dostępu powinny wygasać w ciągu 15 minut. Dłuższy czas życia zwiększa powierzchnię ataku.

  5. Brak mechanizmu unieważniania: Zaimplementuj wylogowanie i unieważnianie tokenów za pomocą Redis lub podobnego magazynu.

Źródła

  • Duende Software. "A step by step JWT authentication example." Duende Software. 2025.
  • Microsoft Learn. "Configure JWT bearer authentication in ASP.NET Core." Microsoft. 2025.
  • Duende Software. "Implementing Token Authentication in Controller-Based ASP.NET Core Web APIs." Duende Software. 2026.
  • QuickNode. "How to Implement JSON Web Tokens (JWT) Authorization." QuickNode. 2025.
  • freeCodeCamp. "How to Build a Secure Authentication System with JWT and Refresh Tokens." freeCodeCamp. 2025.
  • Vonage. "JWT Authentication in Go with Gin." Vonage Developer. 2025.

— Editorial Team

Advertisement 728x90

Czytaj dalej