Александр Гольцман
Работа с JSON Web Tokens в Go
JSON Web Token (JWT) — это компактный формат для безопасной передачи информации между участниками. Он широко используется для аутентификации и авторизации в веб-приложениях. В этой статье я расскажу, как работает JWT, какие у него есть особенности и как его использовать в Go. Мы рассмотрим структуру токена, разберём основные алгоритмы подписи и покажем примеры работы с JWT на практике.
Что такое JSON Web Token (JWT)?
JWT — это стандарт (RFC 7519), который описывает способ передачи данных в формате JSON, защищённый криптографической подписью или шифрованием. Такой подход позволяет передавать информацию между клиентом и сервером без необходимости хранить состояние на сервере.
Токен состоит из трёх частей:
- Header (заголовок) — содержит информацию о типе токена и алгоритме подписи.
- Payload (полезная нагрузка) — включает в себя данные (claims), например, идентификатор пользователя или срок действия токена.
- Signature (подпись) — используется для проверки целостности токена.
Смотрите, как выглядит структура JWT в кодированном виде:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImV4cCI6MTcwOTYwMDAwMH0.3QKxL9Ae2qLZ6a2U6wNOfGQX6qZp8WbD1o0X6K8X7gU
Эта строка состоит из трёх частей, разделённых точками. Каждая часть закодирована в Base64. Декодировав её, можно увидеть JSON-объект с заголовком, данными и подписью.
Как работает JWT?
- Клиент (обычно браузер или мобильное приложение) отправляет серверу запрос с логином и паролем.
- Сервер проверяет данные и, если они верны, создаёт JWT, содержащий информацию о пользователе.
- Токен отправляется клиенту, который сохраняет его (например, в localStorage или HTTP cookie).
- При каждом запросе клиент отправляет токен в заголовке
Authorization: Bearer <токен>
. - Сервер проверяет токен и, если он действителен, выполняет запрос.
Такой механизм позволяет серверу не хранить информацию о сессиях, что делает JWT удобным для масштабируемых систем.
Использование JWT в Go
В Go есть несколько библиотек для работы с JWT, например, github.com/golang-jwt/jwt.
Создание JWT
Вот пример генерации JWT-токена в Go:
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
var secretKey = []byte("my_secret_key")
func generateToken(userID int) (string, error) {
claims := jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 24).Unix(), // Срок действия — 24 часа
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(secretKey)
}
func main() {
token, err := generateToken(123)
if err != nil {
fmt.Println("Ошибка генерации токена:", err)
return
}
fmt.Println("JWT:", token)
}
Здесь я создал токен с user_id
и временем истечения. Он подписан с помощью HS256
и секретного ключа.
Проверка JWT
Смотрите, как можно декодировать и проверить токен:
func parseToken(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
}
Если подпись верна, можно получить claims
и извлечь из них данные.
Использование JWT в HTTP-запросах
JWT обычно передаётся в заголовке Authorization
. Смотрите, как можно извлекать и проверять токен в HTTP-хендлере:
package main
import (
"fmt"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v5"
)
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Токен не предоставлен", http.StatusUnauthorized)
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
token, err := parseToken(tokenString)
if err != nil || !token.Valid {
http.Error(w, "Неверный токен", http.StatusUnauthorized)
return
}
next(w, r)
}
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Вы получили доступ!")
}
func main() {
http.HandleFunc("/protected", authMiddleware(protectedHandler))
http.ListenAndServe(":8080", nil)
}
Здесь я реализовал middleware, который проверяет JWT перед выполнением запроса.
Срок действия и обновление токена
JWT может иметь срок действия (exp
), после которого он становится недействительным. При истечении срока клиент должен обновить токен, например, через refresh-токен.
Обычно это реализуется так:
- Клиент получает access-токен и refresh-токен.
- Когда access-токен истекает, клиент отправляет refresh-токен на сервер.
- Сервер проверяет refresh-токен и, если он действителен, выдаёт новый access-токен.
Этот механизм предотвращает постоянный запрос логина и пароля у пользователя.
Безопасность JWT
Несколько важных рекомендаций по безопасности:
- Используйте безопасные алгоритмы подписи (например, HS256, RS256).
- Храните секретные ключи в защищённом месте (например, в переменных окружения).
- Не храните чувствительные данные в payload, так как его можно декодировать.
- Устанавливайте срок действия токенов, чтобы ограничить их жизненный цикл.
- Используйте refresh-токены вместо продления старых access-токенов.
Если нужно, чтобы токен нельзя было подделать даже при компрометации ключа, можно использовать шифрование (JWE
), но это усложнит обработку.
Заключение
JSON Web Token — мощный инструмент для аутентификации и авторизации. Он позволяет передавать данные между клиентом и сервером без хранения сессий на сервере. В Go JWT легко реализуется с помощью пакета github.com/golang-jwt/jwt
.
Давайте подведём итоги:
- JWT состоит из заголовка, полезной нагрузки и подписи.
- Используется для передачи информации между клиентом и сервером без необходимости хранить состояние на сервере.
- Подписывается с помощью алгоритмов HMAC или RSA для защиты от подделки.
- Содержит срок действия, что предотвращает несанкционированное использование.
- В реальных проектах важно правильно настраивать срок жизни токена и защищать его.
Смотрите, где вам нужно хранить информацию об аутентификации, и выбирайте JWT, если он подходит под вашу задачу.