- Added Oauth2LogLoginService for managing user authorization logs. - Implemented methods for inserting logs, cleaning logs, and exporting log data. - Created a new file for Oauth2 login log service. refactor: Remove unused open_api module - Deleted the open_api.go file as it was not utilized in the project. fix: Update error codes in SysProfileController - Changed error codes for binding errors and user authentication errors to more descriptive values. fix: Update cache handling in SysConfig and SysDictType services - Modified Redis set operations to include expiration time for cached values. refactor: Update middleware authorization checks - Replaced PreAuthorize middleware with AuthorizeUser across multiple routes in system and tool modules for consistency. chore: Clean up trace and ws modules - Updated middleware authorization in trace and ws modules to use AuthorizeUser.
174 lines
5.2 KiB
Go
174 lines
5.2 KiB
Go
package token
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
|
|
"be.ems/src/framework/config"
|
|
"be.ems/src/framework/constants"
|
|
"be.ems/src/framework/database/redis"
|
|
"be.ems/src/framework/logger"
|
|
"be.ems/src/framework/utils/parse"
|
|
)
|
|
|
|
// UserTokenCreate 生成令牌
|
|
// userId 用户ID
|
|
// deviceFingerprint 设备指纹 SHA256
|
|
// tokenType 令牌类型 access:访问令牌 refresh:刷新令牌
|
|
func UserTokenCreate(userId int64, deviceFingerprint, tokenType string) (string, int64) {
|
|
// 令牌算法 HS256 HS384 HS512
|
|
algorithm := config.Get("jwt.algorithm").(string)
|
|
var method *jwt.SigningMethodHMAC
|
|
switch algorithm {
|
|
case "HS512":
|
|
method = jwt.SigningMethodHS512
|
|
case "HS384":
|
|
method = jwt.SigningMethodHS384
|
|
default: // 包含HS256和其他所有情况
|
|
method = jwt.SigningMethodHS256
|
|
}
|
|
|
|
// 生成令牌设置密钥
|
|
secret := fmt.Sprint(config.Get("jwt.secret"))
|
|
// 设置令牌过期时间
|
|
now := time.Now()
|
|
exp := now
|
|
if tokenType == "access" {
|
|
expiresIn := time.Duration(parse.Number(config.Get("jwt.expiresIn")))
|
|
exp = now.Add(expiresIn * time.Minute)
|
|
secret = "User_Access:" + secret
|
|
}
|
|
if tokenType == "refresh" {
|
|
refreshIn := time.Duration(parse.Number(config.Get("jwt.refreshIn")))
|
|
exp = now.Add(refreshIn * time.Minute)
|
|
secret = "User_Refresh:" + secret
|
|
}
|
|
|
|
// 生成令牌负荷绑定uuid标识
|
|
jwtToken := jwt.NewWithClaims(method, jwt.MapClaims{
|
|
constants.JWT_DEVICE_ID: deviceFingerprint,
|
|
constants.JWT_USER_ID: userId,
|
|
"exp": exp.Unix(), // 过期时间
|
|
"iat": now.Unix(), // 签发时间
|
|
"nbf": now.Add(-10 * time.Second).Unix(), // 生效时间
|
|
})
|
|
|
|
tokenStr, err := jwtToken.SignedString([]byte(secret))
|
|
if err != nil {
|
|
logger.Infof("jwt sign err : %v", err)
|
|
return "", 0
|
|
}
|
|
expSeconds := int64(exp.Sub(now).Seconds())
|
|
return tokenStr, expSeconds
|
|
}
|
|
|
|
// UserTokenVerify 校验令牌是否有效
|
|
// tokenType 令牌类型 access:访问令牌 refresh:刷新令牌
|
|
func UserTokenVerify(tokenStr string, tokenType string) (jwt.MapClaims, error) {
|
|
jwtToken, err := jwt.Parse(tokenStr, func(jToken *jwt.Token) (any, error) {
|
|
// 判断加密算法是预期的加密算法
|
|
if _, ok := jToken.Method.(*jwt.SigningMethodHMAC); ok {
|
|
secret := config.Get("jwt.secret").(string)
|
|
if tokenType == "access" {
|
|
secret = "User_Access:" + secret
|
|
}
|
|
if tokenType == "refresh" {
|
|
secret = "User_Refresh:" + secret
|
|
}
|
|
return []byte(secret), nil
|
|
}
|
|
return nil, jwt.ErrSignatureInvalid
|
|
})
|
|
if err != nil {
|
|
logger.Errorf("Token Verify Err: %v", err)
|
|
return nil, fmt.Errorf("token invalid")
|
|
}
|
|
// 如果解析负荷成功并通过签名校验
|
|
claims, ok := jwtToken.Claims.(jwt.MapClaims)
|
|
if ok && jwtToken.Valid {
|
|
return claims, nil
|
|
}
|
|
return nil, fmt.Errorf("token valid error")
|
|
}
|
|
|
|
// UserInfoRemove 清除访问用户信息缓存
|
|
func UserInfoRemove(tokenStr string) (string, error) {
|
|
claims, err := UserTokenVerify(tokenStr, "access")
|
|
if err != nil {
|
|
logger.Errorf("token verify err %v", err)
|
|
return "", err
|
|
}
|
|
info := UserInfoGet(claims)
|
|
if info.User.UserName != "" {
|
|
// 清除缓存KEY
|
|
deviceId := fmt.Sprint(claims[constants.JWT_DEVICE_ID])
|
|
tokenKey := constants.CACHE_TOKEN_DEVICE + ":" + deviceId
|
|
return info.User.UserName, redis.Del("", tokenKey)
|
|
}
|
|
return "", fmt.Errorf("token invalid")
|
|
}
|
|
|
|
// UserInfoCreate 生成访问用户信息缓存
|
|
func UserInfoCreate(info *UserInfo, deviceFingerprint string, ilobArr [4]string) {
|
|
info.DeviceId = deviceFingerprint
|
|
|
|
// 设置请求用户登录客户端
|
|
info.LoginIp = ilobArr[0]
|
|
info.LoginLocation = ilobArr[1]
|
|
info.OS = ilobArr[2]
|
|
info.Browser = ilobArr[3]
|
|
|
|
expiresIn := time.Duration(parse.Number(config.Get("jwt.expiresIn")))
|
|
now := time.Now()
|
|
exp := now.Add(expiresIn * time.Minute)
|
|
info.LoginTime = now.UnixMilli()
|
|
info.ExpireTime = exp.UnixMilli()
|
|
// 设置新登录IP和登录时间
|
|
info.User.LoginIp = info.LoginIp
|
|
info.User.LoginTime = info.LoginTime
|
|
info.User.Password = ""
|
|
// 登录信息标识缓存
|
|
tokenKey := constants.CACHE_TOKEN_DEVICE + ":" + info.DeviceId
|
|
jsonBytes, err := json.Marshal(info)
|
|
if err != nil {
|
|
return
|
|
}
|
|
_ = redis.Set("", tokenKey, string(jsonBytes), expiresIn*time.Minute)
|
|
}
|
|
|
|
// UserInfoUpdate 更新访问用户信息缓存
|
|
func UserInfoUpdate(info UserInfo) {
|
|
info.User.Password = ""
|
|
// 登录信息标识缓存
|
|
tokenKey := constants.CACHE_TOKEN_DEVICE + ":" + info.DeviceId
|
|
jsonBytes, err := json.Marshal(info)
|
|
if err != nil {
|
|
return
|
|
}
|
|
expiresIn, _ := redis.GetExpire("", tokenKey)
|
|
expiration := time.Duration(expiresIn) * time.Second
|
|
_ = redis.Set("", tokenKey, string(jsonBytes), expiration)
|
|
}
|
|
|
|
// UserInfoGet 缓存的访问用户信息
|
|
func UserInfoGet(claims jwt.MapClaims) UserInfo {
|
|
info := UserInfo{}
|
|
deviceId := fmt.Sprint(claims[constants.JWT_DEVICE_ID])
|
|
tokenKey := constants.CACHE_TOKEN_DEVICE + ":" + deviceId
|
|
hasKey, err := redis.Has("", tokenKey)
|
|
if hasKey > 0 && err == nil {
|
|
infoStr, err := redis.Get("", tokenKey)
|
|
if infoStr == "" || err != nil {
|
|
return info
|
|
}
|
|
if err := json.Unmarshal([]byte(infoStr), &info); err != nil {
|
|
logger.Errorf("info json err : %v", err)
|
|
return info
|
|
}
|
|
}
|
|
return info
|
|
}
|