JWT токены в Go (Golang): полное руководство
JSON Web Tokens (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанных на JSON. JWT широко используется для аутентификации и обмена информацией между клиентом и сервером. В языке Go (Golang) работа с JWT токенами реализуется просто и эффективно благодаря библиотеке golang-jwt/jwt.
В этой статье мы подробно разберём создание, подпись, проверку и использование JWT токенов в Go. Материал будет полезен как новичкам, так и опытным разработчикам, которые хотят внедрить безопасную аутентификацию в свои проекты на Golang.
Установка библиотеки golang-jwt
Для работы с JWT в Go используется пакет github.com/golang-jwt/jwt/v5. Установите его командой:
go get github.com/golang-jwt/jwt/v5
После установки можно импортировать пакет в ваш код:
import (
"github.com/golang-jwt/jwt/v5"
)
Структура JWT токена
JWT токен состоит из трёх частей, разделённых точками:
- Header (заголовок) — содержит тип токена (JWT) и алгоритм подписи (например, HS256 или RS256).
- Payload (полезная нагрузка) — содержит данные (claims), такие как идентификатор пользователя, роль, срок действия и т.д.
- Signature (подпись) — создаётся на основе header и payload с использованием секретного ключа или пары ключей RSA/ECDSA.
Создание JWT токена в Go
Рассмотрим пример создания JWT токена с алгоритмом HMAC-SHA256 (HS256). Для этого понадобится секретный ключ.
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
func main() {
// Секретный ключ (должен храниться в безопасном месте)
secretKey := []byte("my_secret_key_12345")
// Создаём claims (утверждения)
claims := jwt.MapClaims{
"user_id": 123,
"role": "admin",
"exp": time.Now().Add(time.Hour * 24).Unix(), // токен действует 24 часа
"iat": time.Now().Unix(),
}
// Создаём токен
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Подписываем токен секретным ключом
tokenString, err := token.SignedString(secretKey)
if err != nil {
fmt.Println("Ошибка создания токена:", err)
return
}
fmt.Println("Созданный JWT токен:")
fmt.Println(tokenString)
}
Проверка и парсинг JWT токена
Для проверки подлинности токена используется тот же секретный ключ. Важно проверять срок действия и подпись.
package main
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
tokenString := "ваш_токен_здесь"
secretKey := []byte("my_secret_key_12345")
// Парсим и проверяем токен
token, err := jwt.Parse(tokenString, 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 {
fmt.Println("Ошибка проверки токена:", err)
return
}
// Извлекаем claims
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
fmt.Println("Токен валиден!")
fmt.Printf("User ID: %v\n", claims["user_id"])
fmt.Printf("Role: %v\n", claims["role"])
} else {
fmt.Println("Токен недействителен")
}
}
Использование структурированных claims
Для удобства можно определить собственную структуру claims, реализующую интерфейс jwt.Claims:
type CustomClaims struct {
UserID int `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func createCustomToken() (string, error) {
claims := CustomClaims{
UserID: 456,
Role: "user",
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "myapp",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte("secret"))
}
JWT с асимметричным шифрованием (RSA)
Для повышения безопасности можно использовать пару клю