Files

68 lines
1.8 KiB
Go
Raw Permalink Normal View History

2026-08-15 17:04:47 +08:00
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
}