Files
tp/main_dc/yalarba/api_yal/internal/middleware/auth.go
T
valitovgaziz 75b2f3f6b2 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/account/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/errors.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/service.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/types.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/admin.go
	modified:   main_dc/yalarba/api_yal/internal/middleware/auth.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/context.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/logging.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
last but not yet commit
2026-03-31 09:43:18 +05:00

154 lines
4.7 KiB
Go

package middleware
import (
"context"
"fmt"
"net/http"
"strings"
"api_yal/internal/logger"
"github.com/golang-jwt/jwt/v5"
"go.uber.org/zap"
)
type contextKey string
const (
UserIDKey contextKey = "userID"
UserEmailKey contextKey = "userEmail"
UserRoleKey contextKey = "userRole"
)
// AuthMiddleware создает middleware для проверки JWT токена
func AuthMiddleware(jwtSecret string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
// Логируем только в debug режиме, чтобы не засорять логи
l.Debug("Auth middleware: processing request",
zap.String("path", r.URL.Path),
zap.String("method", r.Method))
// Получаем токен из заголовка Authorization
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
l.Debug("Authorization header missing")
http.Error(w, "Authorization header required", http.StatusUnauthorized)
return
}
// Ожидаем формат "Bearer <token>"
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
l.Debug("Invalid authorization header format",
zap.String("header", authHeader))
http.Error(w, "Invalid authorization header format", http.StatusUnauthorized)
return
}
tokenString := parts[1]
// Парсим и валидируем токен
claims, err := validateToken(tokenString, jwtSecret)
if err != nil {
l.Debug("Token validation failed", zap.Error(err))
// Возвращаем разные сообщения в зависимости от ошибки
if err == jwt.ErrTokenExpired {
http.Error(w, "Token expired", http.StatusUnauthorized)
return
}
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Добавляем информацию о пользователе в контекст
ctx := addUserToContext(r.Context(), claims)
// Логируем успешную аутентификацию (только в debug)
if userID, ok := ctx.Value(UserIDKey).(uint); ok {
l.Debug("User authenticated",
zap.Uint("user_id", userID),
zap.String("path", r.URL.Path))
}
// Передаем управление дальше с обновленным контекстом
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// validateToken валидирует JWT токен и возвращает claims
func validateToken(tokenString, jwtSecret string) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Проверяем метод подписи
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(jwtSecret), nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("invalid claims")
}
// Проверяем тип токена (должен быть access)
if tokenType, exists := claims["type"]; !exists || tokenType != "access" {
return nil, fmt.Errorf("invalid token type, expected access")
}
return claims, nil
}
// addUserToContext добавляет информацию о пользователе в контекст
func addUserToContext(ctx context.Context, claims jwt.MapClaims) context.Context {
// Извлекаем userID из sub (subject)
if userIDStr, ok := claims["sub"].(string); ok {
var userID uint
if _, err := fmt.Sscan(userIDStr, &userID); err == nil {
ctx = context.WithValue(ctx, UserIDKey, userID)
}
}
// Извлекаем email
if email, ok := claims["email"].(string); ok {
ctx = context.WithValue(ctx, UserEmailKey, email)
}
// Извлекаем роль
if role, ok := claims["role"].(string); ok {
ctx = context.WithValue(ctx, UserRoleKey, role)
}
return ctx
}
// GetUserID извлекает ID пользователя из контекста
func GetUserID(ctx context.Context) (uint, bool) {
userID, ok := ctx.Value(UserIDKey).(uint)
return userID, ok
}
// GetUserEmail извлекает email пользователя из контекста
func GetUserEmail(ctx context.Context) (string, bool) {
email, ok := ctx.Value(UserEmailKey).(string)
return email, ok
}
// GetUserRole извлекает роль пользователя из контекста
func GetUserRole(ctx context.Context) (string, bool) {
role, ok := ctx.Value(UserRoleKey).(string)
return role, ok
}