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.
Open-IM-Server/pkg/common/token_verify/jwt_token.go

320 lines
10 KiB

package token_verify
import (
"Open_IM/pkg/common/config"
"Open_IM/pkg/common/constant"
commonDB "Open_IM/pkg/common/db"
"Open_IM/pkg/common/log"
2 years ago
"Open_IM/pkg/common/tools"
2 years ago
"Open_IM/pkg/common/trace_log"
3 years ago
"Open_IM/pkg/utils"
2 years ago
"context"
"github.com/OpenIMSDK/open_utils"
"time"
go_redis "github.com/go-redis/redis/v8"
"github.com/golang-jwt/jwt/v4"
)
//var (
// TokenExpired = errors.New("token is timed out, please log in again")
// TokenInvalid = errors.New("token has been invalidated")
// TokenNotValidYet = errors.New("token not active yet")
// TokenMalformed = errors.New("that's not even a token")
// TokenUnknown = errors.New("couldn't handle this token")
//)
type Claims struct {
UID string
Platform string //login platform
jwt.RegisteredClaims
}
func BuildClaims(uid, platform string, ttl int64) Claims {
now := time.Now()
before := now.Add(-time.Minute * 5)
return Claims{
UID: uid,
Platform: platform,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(ttl*24) * time.Hour)), //Expiration time
IssuedAt: jwt.NewNumericDate(now), //Issuing time
NotBefore: jwt.NewNumericDate(before), //Begin Effective time
}}
}
func DeleteToken(userID string, platformID int) error {
m, err := commonDB.DB.GetTokenMapByUidPid(userID, constant.PlatformIDToName(platformID))
if err != nil && err != go_redis.Nil {
return utils.Wrap(err, "")
}
var deleteTokenKey []string
for k, v := range m {
_, err = GetClaimFromToken(k)
if err != nil || v != constant.NormalToken {
deleteTokenKey = append(deleteTokenKey, k)
}
}
if len(deleteTokenKey) != 0 {
err = commonDB.DB.DeleteTokenByUidPid(userID, platformID, deleteTokenKey)
return utils.Wrap(err, "")
}
return nil
}
func CreateToken(userID string, platformID int) (string, int64, error) {
claims := BuildClaims(userID, constant.PlatformIDToName(platformID), config.Config.TokenPolicy.AccessExpire)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(config.Config.TokenPolicy.AccessSecret))
if err != nil {
return "", 0, err
}
//remove Invalid token
m, err := commonDB.DB.GetTokenMapByUidPid(userID, constant.PlatformIDToName(platformID))
if err != nil && err != go_redis.Nil {
return "", 0, err
}
var deleteTokenKey []string
for k, v := range m {
_, err = GetClaimFromToken(k)
if err != nil || v != constant.NormalToken {
deleteTokenKey = append(deleteTokenKey, k)
}
}
3 years ago
if len(deleteTokenKey) != 0 {
err = commonDB.DB.DeleteTokenByUidPid(userID, platformID, deleteTokenKey)
if err != nil {
return "", 0, err
}
}
err = commonDB.DB.AddTokenFlag(userID, platformID, tokenString, constant.NormalToken)
if err != nil {
return "", 0, err
}
return tokenString, claims.ExpiresAt.Time.Unix(), err
}
func secret() jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
return []byte(config.Config.TokenPolicy.AccessSecret), nil
}
}
3 years ago
func GetClaimFromToken(tokensString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokensString, &Claims{}, secret())
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
2 years ago
return nil, utils.Wrap(constant.ErrTokenMalformed, "")
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
2 years ago
return nil, utils.Wrap(constant.ErrTokenExpired, "")
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
2 years ago
return nil, utils.Wrap(constant.ErrTokenNotValidYet, "")
} else {
2 years ago
return nil, utils.Wrap(constant.ErrTokenUnknown, "")
}
} else {
2 years ago
return nil, utils.Wrap(constant.ErrTokenUnknown, "")
}
} else {
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
2 years ago
return nil, utils.Wrap(constant.ErrTokenUnknown, "")
}
}
3 years ago
func IsAppManagerAccess(token string, OpUserID string) bool {
claims, err := ParseToken(token, "")
3 years ago
if err != nil {
return false
}
if utils.IsContain(claims.UID, config.Config.Manager.AppManagerUid) && claims.UID == OpUserID {
return true
}
return false
}
3 years ago
func IsManagerUserID(OpUserID string) bool {
3 years ago
if utils.IsContain(OpUserID, config.Config.Manager.AppManagerUid) {
return true
} else {
return false
}
}
2 years ago
func CheckManagerUserID(ctx context.Context) error {
if utils.IsContain(tools.OpUserID(ctx), config.Config.Manager.AppManagerUid) {
return nil
}
return constant.ErrIdentity.Wrap(utils.GetSelfFuncName())
}
2 years ago
func CheckAccess(ctx context.Context, OpUserID string, OwnerUserID string) bool {
3 years ago
if utils.IsContain(OpUserID, config.Config.Manager.AppManagerUid) {
return true
}
if OpUserID == OwnerUserID {
return true
}
return false
}
2 years ago
func CheckAccessV2(ctx context.Context, OpUserID string, OwnerUserID string) (err error) {
defer func() {
2 years ago
trace_log.SetCtxInfo(ctx, utils.GetFuncName(1), err, "OpUserID", OpUserID, "OwnerUserID", OwnerUserID)
2 years ago
}()
2 years ago
if utils.IsContain(OpUserID, config.Config.Manager.AppManagerUid) {
return nil
}
if OpUserID == OwnerUserID {
2 years ago
return nil
}
return utils.Wrap(constant.ErrIdentity, open_utils.GetSelfFuncName())
}
func CheckAccessV3(ctx context.Context, OwnerUserID string) (err error) {
opUserID := tools.OpUserID(ctx)
defer func() {
trace_log.SetCtxInfo(ctx, utils.GetFuncName(1), err, "OpUserID", opUserID, "OwnerUserID", OwnerUserID)
}()
if utils.IsContain(opUserID, config.Config.Manager.AppManagerUid) {
return nil
}
if opUserID == OwnerUserID {
2 years ago
return nil
}
2 years ago
return utils.Wrap(constant.ErrIdentity, open_utils.GetSelfFuncName())
2 years ago
}
3 years ago
func GetUserIDFromToken(token string, operationID string) (bool, string, string) {
claims, err := ParseToken(token, operationID)
3 years ago
if err != nil {
3 years ago
log.Error(operationID, "ParseToken failed, ", err.Error(), token)
3 years ago
return false, "", err.Error()
3 years ago
}
3 years ago
log.Debug(operationID, "token claims.ExpiresAt.Second() ", claims.ExpiresAt.Unix())
3 years ago
return true, claims.UID, ""
3 years ago
}
3 years ago
func ParseUserIDFromToken(token string, operationID string) (string, error) {
claims, err := ParseToken(token, operationID)
if err != nil {
log.Error(operationID, "ParseToken failed, ", err.Error(), token)
return "", err
}
log.Debug(operationID, "token claims.ExpiresAt.Second() ", claims.ExpiresAt.Unix())
return claims.UID, nil
}
3 years ago
func GetUserIDFromTokenExpireTime(token string, operationID string) (bool, string, string, int64) {
claims, err := ParseToken(token, operationID)
if err != nil {
log.Error(operationID, "ParseToken failed, ", err.Error(), token)
return false, "", err.Error(), 0
}
return true, claims.UID, "", claims.ExpiresAt.Unix()
3 years ago
}
3 years ago
func ParseTokenGetUserID(token string, operationID string) (error, string) {
claims, err := ParseToken(token, operationID)
if err != nil {
return utils.Wrap(err, ""), ""
}
return nil, claims.UID
}
func ParseToken(tokensString, operationID string) (claims *Claims, err error) {
3 years ago
claims, err = GetClaimFromToken(tokensString)
if err != nil {
3 years ago
return nil, utils.Wrap(err, "")
}
m, err := commonDB.DB.GetTokenMapByUidPid(claims.UID, claims.Platform)
if err != nil {
log.NewError(operationID, "get token from redis err", err.Error(), claims.UID, claims.Platform)
2 years ago
return nil, utils.Wrap(constant.ErrTokenNotExist, "get token from redis err")
}
if m == nil {
log.NewError(operationID, "get token from redis err, not in redis ", "m is nil ", claims.UID, claims.Platform)
2 years ago
return nil, utils.Wrap(constant.ErrTokenNotExist, "get token from redis err")
}
if v, ok := m[tokensString]; ok {
switch v {
case constant.NormalToken:
log.NewDebug(operationID, "this is normal return ", *claims)
return claims, nil
case constant.KickedToken:
log.Error(operationID, "this token has been kicked by other same terminal ", constant.ErrTokenKicked)
2 years ago
return nil, utils.Wrap(constant.ErrTokenKicked, "this token has been kicked by other same terminal ")
default:
2 years ago
return nil, utils.Wrap(constant.ErrTokenUnknown, "")
}
}
log.NewError(operationID, "redis token map not find ", constant.ErrTokenNotExist, tokensString)
2 years ago
return nil, utils.Wrap(constant.ErrTokenNotExist, "redis token map not find")
}
//func MakeTheTokenInvalid(currentClaims *Claims, platformClass string) (bool, error) {
// storedRedisTokenInterface, err := db.DB.GetPlatformToken(currentClaims.UID, platformClass)
// if err != nil {
// return false, err
// }
// storedRedisPlatformClaims, err := ParseRedisInterfaceToken(storedRedisTokenInterface)
// if err != nil {
// return false, err
// }
// //if issue time less than redis token then make this token invalid
// if currentClaims.IssuedAt.Time.Unix() < storedRedisPlatformClaims.IssuedAt.Time.Unix() {
// return true, constant.TokenInvalid
// }
// return false, nil
//}
func ParseRedisInterfaceToken(redisToken interface{}) (*Claims, error) {
3 years ago
return GetClaimFromToken(string(redisToken.([]uint8)))
}
// Validation token, false means failure, true means successful verification
func VerifyToken(token, uid string) (bool, error) {
claims, err := ParseToken(token, "")
if err != nil {
3 years ago
return false, utils.Wrap(err, "ParseToken failed")
}
if claims.UID != uid {
2 years ago
return false, constant.ErrTokenUnknown
}
log.NewDebug("", claims.UID, claims.Platform)
return true, nil
}
2 years ago
3 years ago
func WsVerifyToken(token, uid string, platformID string, operationID string) (bool, error, string) {
argMsg := "args: token: " + token + " operationID: " + operationID + " userID: " + uid + " platformID: " + constant.PlatformIDToName(utils.StringToInt(platformID))
3 years ago
claims, err := ParseToken(token, operationID)
if err != nil {
//if errors.Is(err, constant.ErrTokenUnknown) {
// errMsg := "ParseToken failed ErrTokenUnknown " + err.Error()
// log.Error(operationID, errMsg)
//}
//e := errors.Unwrap(err)
//if errors.Is(e, constant.ErrTokenUnknown) {
// errMsg := "ParseToken failed ErrTokenUnknown " + e.Error()
// log.Error(operationID, errMsg)
//}
2 years ago
2 years ago
errMsg := "parse token err " + err.Error() + argMsg
return false, utils.Wrap(err, errMsg), errMsg
}
if claims.UID != uid {
errMsg := " uid is not same to token uid " + argMsg + " claims.UID: " + claims.UID
2 years ago
return false, utils.Wrap(constant.ErrTokenDifferentUserID, errMsg), errMsg
}
if claims.Platform != constant.PlatformIDToName(utils.StringToInt(platformID)) {
errMsg := " platform is not same to token platform " + argMsg + " claims platformID: " + claims.Platform
2 years ago
return false, utils.Wrap(constant.ErrTokenDifferentPlatformID, errMsg), errMsg
}
3 years ago
log.NewDebug(operationID, utils.GetSelfFuncName(), " check ok ", claims.UID, uid, claims.Platform)
return true, nil, ""
}