低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

125 lines
3.3 KiB

package token
import (
"crypto/sha256"
"encoding/hex"
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
var (
jwtSecret []byte
accessExpireH = 7 * 24 // 默认 7 天
refreshExpireH = 30 * 24 // 默认 30 天
)
// Init 初始化 JWT 密钥与有效期(main 启动时从配置加载)
func Init(secret string, accessH, refreshH int) {
jwtSecret = []byte(secret)
if accessH > 0 {
accessExpireH = accessH
}
if refreshH > 0 {
refreshExpireH = refreshH
}
}
// Claims access token 载荷
type Claims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Roles []string `json:"roles"`
SessionID int64 `json:"session_id"`
jwt.RegisteredClaims
}
// RefreshClaims refresh token 载荷
type RefreshClaims struct {
UserID int64 `json:"user_id"`
SessionID int64 `json:"session_id"`
jwt.RegisteredClaims
}
// GenerateToken 生成 access + refresh token
func GenerateToken(userID, sessionID int64, username string, roles []string) (string, string, error) {
now := time.Now()
claims := Claims{
UserID: userID,
Username: username,
Roles: roles,
SessionID: sessionID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(accessExpireH) * time.Hour)),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
},
}
access, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(jwtSecret)
if err != nil {
return "", "", err
}
refreshClaims := RefreshClaims{
UserID: userID,
SessionID: sessionID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(refreshExpireH) * time.Hour)),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
},
}
refresh, err := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims).SignedString(jwtSecret)
if err != nil {
return "", "", err
}
return access, refresh, nil
}
// ParseToken 解析并校验 access token
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, keyFunc)
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
// ParseRefreshToken 解析并校验 refresh token,返回用户 ID 和会话 ID
func ParseRefreshToken(tokenString string) (int64, int64, error) {
token, err := jwt.ParseWithClaims(tokenString, &RefreshClaims{}, keyFunc)
if err != nil {
return 0, 0, err
}
if claims, ok := token.Claims.(*RefreshClaims); ok && token.Valid {
return claims.UserID, claims.SessionID, nil
}
return 0, 0, errors.New("invalid refresh token")
}
func keyFunc(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return jwtSecret, nil
}
// GenerateShortID 生成 token 的短摘要,作为 Redis 存储 key 的一部分
func GenerateShortID(tokenString string) string {
hash := sha256.Sum256([]byte(tokenString))
return hex.EncodeToString(hash[:8])
}
// AccessExpireSeconds 返回 access token 有效期(秒),用于 Redis TTL
func AccessExpireSeconds() uint {
return uint(accessExpireH * 3600)
}
// RefreshExpireSeconds 返回 refresh token 有效期(秒),用于 Redis TTL
func RefreshExpireSeconds() uint {
return uint(refreshExpireH * 3600)
}