192 lines
4.5 KiB
Go
192 lines
4.5 KiB
Go
package jwtutil
|
||
|
||
import (
|
||
"awesomeProject/internal/common"
|
||
"errors"
|
||
"fmt"
|
||
"reflect"
|
||
"time"
|
||
|
||
"github.com/golang-jwt/jwt/v5"
|
||
)
|
||
|
||
// getSecretKey 获取密钥
|
||
func getSecretKey() []byte {
|
||
if common.MineConfig != nil {
|
||
return []byte(common.MineConfig.JWT.SecretKey)
|
||
}
|
||
// 默认密钥(仅开发环境使用)
|
||
return []byte("your-very-secret-key-32-bytes")
|
||
}
|
||
|
||
// getTokenExpireDuration 获取Token过期时间
|
||
func getTokenExpireDuration() time.Duration {
|
||
if common.MineConfig != nil {
|
||
return time.Duration(common.MineConfig.JWT.ExpireHours) * time.Hour
|
||
}
|
||
// 默认24小时
|
||
return time.Hour * 24
|
||
}
|
||
|
||
// 通用:生成 Token,支持任意结构体
|
||
func GenerateToken(claims interface{}) (string, error) {
|
||
// 1. 将结构体转换为 map[string]interface{}
|
||
claimMap, err := structToMap(claims)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 2. 添加标准声明(exp, iat, nbf)
|
||
claimMap["exp"] = time.Now().Add(getTokenExpireDuration()).Unix()
|
||
claimMap["iat"] = time.Now().Unix()
|
||
claimMap["nbf"] = time.Now().Unix()
|
||
|
||
// 3. 创建 Token
|
||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(claimMap))
|
||
|
||
// 4. 签名
|
||
return token.SignedString(getSecretKey())
|
||
}
|
||
|
||
func ParseTokenToMap(tokenString string) (map[string]interface{}, error) {
|
||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||
}
|
||
return getSecretKey(), nil
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if !token.Valid {
|
||
return nil, errors.New("token is invalid")
|
||
}
|
||
|
||
claims, ok := token.Claims.(jwt.MapClaims)
|
||
if !ok {
|
||
return nil, errors.New("token claims is invalid")
|
||
}
|
||
|
||
return claims, nil
|
||
}
|
||
|
||
func ParseToken(tokenString string, dest interface{}) error {
|
||
|
||
if reflect.TypeOf(dest).Kind() != reflect.Ptr {
|
||
return errors.New("dest must be a pointer to a struct")
|
||
}
|
||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||
}
|
||
return getSecretKey(), nil
|
||
})
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if !token.Valid {
|
||
return errors.New("token 无效")
|
||
}
|
||
|
||
// 提取 MapClaims
|
||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||
// 将 MapClaims 转回结构体
|
||
if err := mapToStruct(claims, dest); err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
return errors.New("无法解析 token 声明")
|
||
}
|
||
|
||
// 将结构体转为 map[string]interface{}
|
||
func structToMap(obj interface{}) (map[string]interface{}, error) {
|
||
val := reflect.ValueOf(obj)
|
||
if val.Kind() == reflect.Ptr {
|
||
val = val.Elem()
|
||
}
|
||
|
||
if val.Kind() != reflect.Struct {
|
||
return nil, errors.New("输入必须是结构体")
|
||
}
|
||
|
||
result := make(map[string]interface{})
|
||
typ := val.Type()
|
||
for i := 0; i < val.NumField(); i++ {
|
||
field := val.Field(i)
|
||
fieldType := typ.Field(i)
|
||
jsonTag := fieldType.Tag.Get("json")
|
||
if jsonTag == "" || jsonTag == "-" {
|
||
continue
|
||
}
|
||
key := jsonTag
|
||
result[key] = field.Interface()
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// 将 map[string]interface{} 转回结构体
|
||
func mapToStruct(data map[string]interface{}, dest interface{}) error {
|
||
val := reflect.ValueOf(dest)
|
||
if val.Kind() != reflect.Ptr {
|
||
return errors.New("dest 必须是指针")
|
||
}
|
||
val = val.Elem()
|
||
if val.Kind() != reflect.Struct {
|
||
return errors.New("dest 必须是指针指向结构体")
|
||
}
|
||
|
||
for i := 0; i < val.NumField(); i++ {
|
||
field := val.Field(i)
|
||
fieldType := val.Type().Field(i)
|
||
jsonTag := fieldType.Tag.Get("json")
|
||
if jsonTag == "" || jsonTag == "-" {
|
||
continue
|
||
}
|
||
|
||
if field.CanSet() {
|
||
value, ok := data[jsonTag]
|
||
if !ok {
|
||
continue
|
||
}
|
||
|
||
// 自动类型转换
|
||
switch field.Kind() {
|
||
case reflect.String:
|
||
if s, ok := value.(string); ok {
|
||
field.SetString(s)
|
||
}
|
||
case reflect.Int, reflect.Int64:
|
||
if n, ok := value.(float64); ok {
|
||
field.SetInt(int64(n))
|
||
}
|
||
case reflect.Uint, reflect.Uint64:
|
||
if n, ok := value.(float64); ok {
|
||
field.SetUint(uint64(n))
|
||
}
|
||
case reflect.Bool:
|
||
if b, ok := value.(bool); ok {
|
||
field.SetBool(b)
|
||
}
|
||
case reflect.Float64:
|
||
if f, ok := value.(float64); ok {
|
||
field.SetFloat(f)
|
||
}
|
||
default:
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
func ValidateToken(tokenString string, dest interface{}) bool {
|
||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||
return getSecretKey(), nil
|
||
})
|
||
return err == nil && token.Valid && ParseToken(tokenString, dest) == nil
|
||
}
|