JWT токены в Go (Golang): библиотека golang-jwt и примеры использования

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

JWT токены в Go (Golang): Полное руководство с библиотекой golang-jwt

JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанных на JSON. JWT широко используется для аутентификации и обмена информацией между сторонами. В Go (Golang) наиболее популярной и активно поддерживаемой библиотекой для работы с JWT является golang-jwt/jwt/v5. В этой статье мы подробно разберем, как устанавливать, настраивать и использовать эту библиотеку для создания безопасных веб-приложений.

Установка библиотеки golang-jwt

Для начала работы добавьте библиотеку в ваш проект с помощью go get:

go get github.com/golang-jwt/jwt/v5

После установки импортируйте пакет в ваш код:

import (
    "github.com/golang-jwt/jwt/v5"
)

Основные компоненты JWT

JWT состоит из трех частей, разделенных точками: Header (заголовок), Payload (полезная нагрузка) и Signature (подпись). Библиотека golang-jwt предоставляет удобные структуры для работы с каждой из них:

  • Header — содержит метаданные о типе токена и алгоритме подписи (например, HS256, RS256).
  • Payload — содержит claims (утверждения), такие как идентификатор пользователя, время истечения, роли и другие данные.
  • Signature — создается с использованием секретного ключа или пары ключей (приватный/публичный).

Создание JWT токена доступа (Access Token)

Рассмотрим пример создания токена доступа с алгоритмом HS256 (симметричное шифрование). Сначала определим структуру с кастомными claims:

type CustomClaims struct {
    UserID   int    `json:"user_id"`
    Username string `json:"username"`
    jwt.RegisteredClaims
}

Теперь создадим функцию для генерации токена:

func GenerateAccessToken(userID int, username string, secretKey []byte) (string, error) {
    claims := CustomClaims{
        UserID:   userID,
        Username: username,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            Subject:   "access",
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(secretKey)
}

Здесь мы устанавливаем время жизни токена 15 минут, что является стандартной практикой для access token. Используйте jwt.NewNumericDate() для корректного форматирования времени.

Верификация и парсинг токена

Для проверки подлинности токена необходимо извлечь claims и проверить подпись:

func ValidateToken(tokenString string, secretKey []byte) (*CustomClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
        // Проверяем, что используется ожидаемый алгоритм подписи
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("неожиданный метод подписи: %v", token.Header["alg"])
        }
        return secretKey, nil
    })

    if err != nil {
        return nil, err
    }

    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
        return claims, nil
    }

    return nil, fmt.Errorf("невалидный токен")
}

Важно проверять алгоритм подписи в callback-функции, чтобы избежать атак, связанных с подменой алгоритма.

Создание и обновление Refresh Token

Для повышения безопасности часто используют пару токенов: короткоживущий access token и долгоживущий refresh token. Refresh token позволяет получить новый access token без повторного ввода логина/пароля.

func GenerateRefreshToken(userID int, secretKey []byte) (string, error) {
    claims := jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)), // 7 дней
        IssuedAt:  jwt.NewNumericDate(time.Now()),
        Subject:   "refresh",
        ID:        strconv.Itoa(userID),
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(secretKey)
}

Для обновления access token с помощью refresh token:

func RefreshAccessToken(refreshTokenString string, secretKey []byte) (string, error) {
    // Парсим refresh token
    refreshClaims := &jwt.RegisteredClaims{}
    _, err := jwt.ParseWithClaims(refreshTokenString, refreshClaims, func(token *jwt.Token) (interface{}, error) {
        return secretKey, nil
    })
    if err != nil {
        return "", fmt.Errorf("невалидный refresh token: %w", err)
    }

    // Извлекаем userID из Subject (или из собственного                    

Рекомендаций