package auth import ( "crypto/subtle" "database/sql" "net/http" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) type Auth struct { secret []byte } func New(secret string) *Auth { return &Auth{secret: []byte(secret)} } func (a *Auth) Issue(userID string) (string, error) { t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "sub": userID, "iat": time.Now().Unix(), "exp": time.Now().Add(7 * 24 * time.Hour).Unix(), }) return t.SignedString(a.secret) } func (a *Auth) parseToken(token string) (string, error) { parsed, err := jwt.Parse(token, func(t *jwt.Token) (any, error) { if t.Method.Alg() != jwt.SigningMethodHS256.Alg() { return nil, jwt.ErrSignatureInvalid } return a.secret, nil }) if err != nil { return "", err } if !parsed.Valid { return "", jwt.ErrTokenInvalidClaims } claims, ok := parsed.Claims.(jwt.MapClaims) if !ok { return "", jwt.ErrTokenInvalidClaims } sub, ok := claims["sub"].(string) if !ok || sub == "" { return "", jwt.ErrTokenInvalidClaims } return sub, nil } func (a *Auth) RequireAuth(sqlite *sql.DB) gin.HandlerFunc { return func(c *gin.Context) { hdr := c.GetHeader("Authorization") if hdr == "" || !strings.HasPrefix(hdr, "Bearer ") { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "未登录"}) return } token := strings.TrimSpace(strings.TrimPrefix(hdr, "Bearer ")) userID, err := a.parseToken(token) if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "Token 无效"}) return } var exists int if err := sqlite.QueryRow(`SELECT COUNT(1) FROM users WHERE id = ?`, userID).Scan(&exists); err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "服务异常"}) return } if subtle.ConstantTimeEq(int32(exists), 1) != 1 { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "账号不存在"}) return } c.Set("userID", userID) c.Next() } } func UserID(c *gin.Context) string { v, _ := c.Get("userID") s, _ := v.(string) return s }