Initial commit: GovAI 政务AI平台
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type TokenPair struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type JWTManager struct {
|
||||
secret []byte
|
||||
accessExpiry time.Duration
|
||||
refreshExpiry time.Duration
|
||||
}
|
||||
|
||||
func NewJWTManager(secret string, accessExpiry, refreshExpiry time.Duration) *JWTManager {
|
||||
return &JWTManager{
|
||||
secret: []byte(secret),
|
||||
accessExpiry: accessExpiry,
|
||||
refreshExpiry: refreshExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *JWTManager) GenerateTokenPair(userID uuid.UUID, email, role string) (*TokenPair, error) {
|
||||
now := time.Now()
|
||||
accessExp := now.Add(m.accessExpiry)
|
||||
|
||||
accessClaims := &Claims{
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
Role: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(accessExp),
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
Subject: userID.String(),
|
||||
},
|
||||
}
|
||||
|
||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
|
||||
accessStr, err := accessToken.SignedString(m.secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sign access token: %w", err)
|
||||
}
|
||||
|
||||
refreshClaims := &Claims{
|
||||
UserID: userID,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(m.refreshExpiry)),
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
Subject: userID.String(),
|
||||
},
|
||||
}
|
||||
|
||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
|
||||
refreshStr, err := refreshToken.SignedString(m.secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sign refresh token: %w", err)
|
||||
}
|
||||
|
||||
return &TokenPair{
|
||||
AccessToken: accessStr,
|
||||
RefreshToken: refreshStr,
|
||||
ExpiresAt: accessExp.Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *JWTManager) ValidateToken(tokenStr string) (*Claims, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (any, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return m.secret, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse token: %w", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok || !token.Valid {
|
||||
return nil, fmt.Errorf("invalid token claims")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package auth
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
func CheckPassword(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package auth
|
||||
|
||||
import "context"
|
||||
|
||||
// SSOProvider defines the interface for SSO authentication providers.
|
||||
// Implementations will be added for LDAP, OAuth2, and SAML.
|
||||
type SSOProvider interface {
|
||||
// Authenticate validates credentials and returns user information.
|
||||
Authenticate(ctx context.Context, credentials map[string]string) (*SSOUser, error)
|
||||
// Name returns the provider name (e.g., "ldap", "oauth2", "saml").
|
||||
Name() string
|
||||
}
|
||||
|
||||
type SSOUser struct {
|
||||
ExternalID string
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
Department string
|
||||
AvatarURL string
|
||||
}
|
||||
Reference in New Issue
Block a user