68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
|
|
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
|
|||
|
|
}
|