package commonservice import ( "time" "github.com/golang-jwt/jwt/v5" ) // Claims 是 access / refresh token 的载荷。 type Claims struct { UserID int64 `json:"userId"` Username string `json:"username"` Type string `json:"type"` // access | refresh jwt.RegisteredClaims } // IssueAccess 签发 access token。 func IssueAccess(secret string, userID int64, username string, ttlHours int) (string, error) { if ttlHours <= 0 { ttlHours = 2 } return issue(secret, userID, username, "access", time.Duration(ttlHours)*time.Hour) } // IssueRefresh 签发 refresh token。 func IssueRefresh(secret string, userID int64, username string, ttlDays int) (string, error) { if ttlDays <= 0 { ttlDays = 30 } return issue(secret, userID, username, "refresh", time.Duration(ttlDays)*24*time.Hour) } func issue(secret string, userID int64, username, typ string, ttl time.Duration) (string, error) { now := time.Now() claims := Claims{ UserID: userID, Username: username, Type: typ, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(ttl)), }, } t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return t.SignedString([]byte(secret)) } // Parse 校验并解析 JWT;typ 非空时要求 claims.Type 匹配。 func Parse(secret, token, typ string) (*Claims, error) { parsed, err := jwt.ParseWithClaims(token, &Claims{}, func(t *jwt.Token) (any, error) { if t.Method != jwt.SigningMethodHS256 { return nil, Unauthorized("UNAUTHORIZED") } return []byte(secret), nil }) if err != nil || !parsed.Valid { return nil, Unauthorized("UNAUTHORIZED") } claims, ok := parsed.Claims.(*Claims) if !ok || claims.UserID <= 0 { return nil, Unauthorized("UNAUTHORIZED") } if typ != "" && claims.Type != typ { return nil, Unauthorized("UNAUTHORIZED") } return claims, nil }