ulflow_phattt2901 f4ef71b63b
Some checks failed
CI Pipeline / Security Scan (push) Failing after 5m24s
CI Pipeline / Lint (push) Failing after 5m30s
CI Pipeline / Test (push) Has been skipped
CI Pipeline / Build (push) Has been skipped
CI Pipeline / Notification (push) Successful in 1s
feat: implement user authentication system with JWT and role-based access control
2025-05-24 11:24:19 +07:00

122 lines
2.9 KiB
Go

package middleware
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"starter-kit/internal/service"
)
const (
// ContextKeyUser là key dùng để lưu thông tin user trong context
ContextKeyUser = "user"
)
// AuthMiddleware xác thực JWT token
type AuthMiddleware struct {
authSvc service.AuthService
}
// NewAuthMiddleware tạo mới AuthMiddleware
func NewAuthMiddleware(authSvc service.AuthService) *AuthMiddleware {
return &AuthMiddleware{
authSvc: authSvc,
}
}
// Authenticate xác thực JWT token
func (m *AuthMiddleware) Authenticate() gin.HandlerFunc {
return func(c *gin.Context) {
// Lấy token từ header
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
return
}
// Kiểm tra định dạng token
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"})
return
}
tokenString := parts[1]
// Xác thực token
claims, err := m.authSvc.ValidateToken(tokenString)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
return
}
// Lưu thông tin user vào context
c.Set(ContextKeyUser, claims)
// Tiếp tục xử lý request
c.Next()
}
}
// RequireRole kiểm tra user có vai trò được yêu cầu không
func (m *AuthMiddleware) RequireRole(roles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
// Lấy thông tin user từ context
userValue, exists := c.Get(ContextKeyUser)
if !exists {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
// Ép kiểu về Claims
claims, ok := userValue.(*service.Claims)
if !ok {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Invalid user data"})
return
}
// Kiểm tra vai trò
for _, role := range roles {
for _, userRole := range claims.Roles {
if userRole == role {
// Có quyền, tiếp tục xử lý
c.Next()
return
}
}
}
// Không có quyền
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": fmt.Sprintf("Require one of these roles: %v", roles),
})
}
}
// GetUserFromContext lấy thông tin user từ context
func GetUserFromContext(c *gin.Context) (*service.Claims, error) {
userValue, exists := c.Get(ContextKeyUser)
if !exists {
return nil, fmt.Errorf("user not found in context")
}
claims, ok := userValue.(*service.Claims)
if !ok {
return nil, fmt.Errorf("invalid user data in context")
}
return claims, nil
}
// GetUserIDFromContext lấy user ID từ context
func GetUserIDFromContext(c *gin.Context) (string, error) {
claims, err := GetUserFromContext(c)
if err != nil {
return "", err
}
return claims.UserID, nil
}