feat: завершён этап 2 - Аутентификация Core Service

- Реализована JWT аутентификация с organization-scope
- Добавлено хеширование паролей через bcrypt
- Созданы репозитории для организаций и пользователей
- Реализован AuthService с бизнес-логикой
- Добавлен AuthMiddleware для проверки токенов
- Созданы handlers для регистрации и входа
- Обновлён API сервер для использования аутентификации

Готово для этапа 3 - API структура
This commit is contained in:
2025-08-27 14:56:33 +04:00
parent 9777114e16
commit ae84ce74a7
11 changed files with 581 additions and 34 deletions

View File

@@ -0,0 +1,69 @@
package auth
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
type Claims struct {
UserID uuid.UUID `json:"user_id"`
OrganizationID uuid.UUID `json:"organization_id"`
Email string `json:"email"`
Role string `json:"role"`
jwt.RegisteredClaims
}
type JWTService struct {
secret string
ttl time.Duration
}
func NewJWTService(secret string, ttl time.Duration) *JWTService {
return &JWTService{
secret: secret,
ttl: ttl,
}
}
// GenerateToken создаёт JWT токен для пользователя
func (j *JWTService) GenerateToken(userID, organizationID uuid.UUID, email, role string) (string, error) {
claims := Claims{
UserID: userID,
OrganizationID: organizationID,
Email: email,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.ttl)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "erp-mvp-core",
Subject: userID.String(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(j.secret))
}
// ValidateToken валидирует JWT токен и возвращает claims
func (j *JWTService) ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(j.secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}

View File

@@ -0,0 +1,17 @@
package auth
import (
"golang.org/x/crypto/bcrypt"
)
// HashPassword хеширует пароль с использованием bcrypt
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
// CheckPassword проверяет пароль против хеша
func CheckPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}