diff --git a/crypto/encipher/encipher.go b/crypto/encipher/encipher.go index c14c028..024c8c1 100644 --- a/crypto/encipher/encipher.go +++ b/crypto/encipher/encipher.go @@ -28,7 +28,7 @@ func New(secret string) { func GenerateTokenAes(id uint, identity, client, role string, owner any, extend map[string]string) (string, error) { if !(JwtSecretLen == 16 || JwtSecretLen == 24 || JwtSecretLen == 32) { - return "", errcode.ErrJWTSecretKey + return "", errcode.ErrTokenSecretKey } expireTime := time.Now().Add(vars.JwtExpire) claims := types.JwtClaims{ @@ -43,7 +43,7 @@ func GenerateTokenAes(id uint, identity, client, role string, owner any, extend byte, err := json.Marshal(claims) if err != nil { - return "", errcode.ErrJWTJsonEncode + return "", errcode.ErrTokenJsonEncode } token, err := AesEncryptCBC(byte) @@ -59,7 +59,7 @@ func AesEncryptCBC(plan []byte) (string, error) { // NewCipher该函数限制了输入k的长度必须为16, 24或者32 block, err := aes.NewCipher(JwtSecret) if err != nil { - return "", errcode.ErrJWTSecretKey + return "", errcode.ErrTokenSecretKey } // 获取秘钥块的长度 blockSize := block.BlockSize() @@ -76,17 +76,17 @@ func AesEncryptCBC(plan []byte) (string, error) { func AesDecryptCBC(cryted string) (b []byte, err error) { if (JwtSecretLen == 16 || JwtSecretLen == 24 || JwtSecretLen == 32) == false { - return nil, errcode.ErrJWTSecretKey + return nil, errcode.ErrTokenSecretKey } // 转成字节数组 crytedByte, err := base64.StdEncoding.DecodeString(cryted) if err != nil { - return nil, errcode.ErrJWTBase64Decode + return nil, errcode.ErrTokenBase64Decode } // 分组秘钥 block, err := aes.NewCipher(JwtSecret) if err != nil { - return nil, errcode.ErrJWTSecretKey + return nil, errcode.ErrTokenSecretKey } // 获取秘钥块的长度 blockSize := block.BlockSize() @@ -99,7 +99,7 @@ func AesDecryptCBC(cryted string) (b []byte, err error) { // 去补全码 orig = PKCS7UnPadding(orig, blockSize) if orig == nil { - return nil, errcode.ErrJWTAuthParseFail + return nil, errcode.ErrTokenAuthParseFail } return orig, nil } @@ -152,12 +152,12 @@ func ParseTokenAes(token string) (*types.JwtClaims, error) { var ac *types.JwtClaims err = json.Unmarshal(data, &ac) if err != nil { - return nil, errcode.ErrJWTAuthParseFail + return nil, errcode.ErrTokenAuthParseFail } expireTime := time.Now().Unix() if expireTime > ac.ExpiresAt { - return nil, errcode.ErrJWTAuthExpire + return nil, errcode.ErrTokenAuthExpire } return ac, nil diff --git a/crypto/token/jwt.go b/crypto/token/jwt.go new file mode 100644 index 0000000..cf46001 --- /dev/null +++ b/crypto/token/jwt.go @@ -0,0 +1,95 @@ +package token + +import ( + "encoding/base64" + "encoding/json" + "strings" + "time" + + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/vars" + "github.com/golang-jwt/jwt/v5" +) + +type Claims struct { + ID uint `json:"id"` + Identity string `json:"identity"` + Extend map[string]string `json:"extend"` + Client string `json:"client"` + Owner any `json:"owner"` + Role string `json:"role"` + jwt.RegisteredClaims // v5版本新加的方法 +} + +type tokenJwt struct { + SecretKey string +} + +func New(secretKey string) *tokenJwt { + return &tokenJwt{SecretKey: secretKey} +} + +// 生成JWT +func (t *tokenJwt) GenerateJwt(id uint, identity, client, role string, owner any, extend map[string]string) (string, error) { + keyLen := len(t.SecretKey) + if !(keyLen == 16 || keyLen == 24 || keyLen == 32) { + return "", errcode.ErrTokenSecretKey + } + + now := time.Now() + + claims := Claims{ + ID: id, + Identity: identity, + Client: client, + Extend: extend, + Owner: owner, + Role: role, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(now.Add(vars.JwtExpire)), // 过期时间24小时 + IssuedAt: jwt.NewNumericDate(now), // 签发时间 + NotBefore: jwt.NewNumericDate(now), // 生效时间 + }, + } + // 使用HS256签名算法 + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + s, err := token.SignedString([]byte(t.SecretKey)) + return s, errcode.String(errcode.ErrTokenGenerate, err.Error()) +} + +// 解析JWT +func (t *tokenJwt) ParseJwt(tokenstring string) (*Claims, error) { + token, err := jwt.ParseWithClaims(tokenstring, &Claims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(t.SecretKey), nil + }) + if claims, ok := token.Claims.(*Claims); ok && token.Valid { + return claims, nil + } else { + return nil, errcode.String(errcode.ErrTokenParse, err.Error()) + } +} + +// 验证JWT是否过期 +func (t *tokenJwt) IsExpired(tokenstring string) (bool, error) { + // 分割JWT的三个部分 + parts := strings.Split(tokenstring, ".") + if len(parts) != 3 { + return false, errcode.ErrTokenDataInvalid + } + + // 解码Payload部分 + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return false, errcode.String(errcode.ErrTokenBase64Decode, err.Error()) + } + + // 解析JSON + var claims jwt.RegisteredClaims + if err := json.Unmarshal(payload, &claims); err != nil { + return false, errcode.String(errcode.ErrTokenJsonDecode, err.Error()) + } + + // 检查过期时间 + currentTime := time.Now().Unix() + return claims.ExpiresAt.Unix() < currentTime, nil +} diff --git a/errcode/errcode.go b/errcode/errcode.go index 3eedd97..869265b 100644 --- a/errcode/errcode.go +++ b/errcode/errcode.go @@ -3,8 +3,6 @@ package errcode import ( - "strings" - "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -33,18 +31,22 @@ var ( ErrRecordNotFound = NewError(1112, "Record Not Found") // 记录未找到 ) -// JWT认证相关错误码,起始码:1300 +// Token认证相关错误码,起始码:1300 var ( - ErrJWTAuthNotFound = NewError(1301, "JWT Authorization Not Found") // JWT授权未找到 - ErrJWTBase64Decode = NewError(1302, "JWT Authorization Base64 Decode Error") // JWT Base64解码错误 - ErrJWTAuthParseFail = NewError(1303, "JWT Authorization Fail") // JWT授权解析失败 - ErrJWTAuthKeyId = NewError(1304, "JWT Key:Id Incorrect") // JWT密钥ID错误 - ErrJWTAuthKeyIdentity = NewError(1305, "JWT Key:Identity Incorrect") // JWT密钥身份错误 - ErrJWTAuthTokenChanged = NewError(1306, "JWT Authorization Changed") // JWT授权已变更 - ErrJWTAuthExpire = NewError(1307, "JWT Authorization Expire") // JWT授权已过期 - ErrJWTJsonDecode = NewError(1308, "JWT Authorization JSON Decode Error") // JWT JSON解码错误 - ErrJWTJsonEncode = NewError(1309, "JWT Authorization JSON Encode Error") // JWT JSON编码错误 - ErrJWTSecretKey = NewError(1310, "JWT SecretKey Error") // JWT密钥错误 + ErrTokenAuthNotFound = NewError(1301, "Token Authorization Not Found") // Token授权未找到 + ErrTokenDataInvalid = NewError(1302, "Token Authorization Data Invalid") // Token授权数据无效 + ErrTokenBase64Decode = NewError(1303, "Token Authorization Base64 Decode Error") // Token Base64解码错误 + ErrTokenAuthParseFail = NewError(1304, "Token Authorization Fail") // Token授权解析失败 + ErrTokenAuthKeyId = NewError(1305, "Token Key:Id Incorrect") // Token密钥ID错误 + ErrTokenAuthKeyIdentity = NewError(1306, "Token Key:Identity Incorrect") // Token密钥身份错误 + ErrTokenAuthTokenChanged = NewError(1307, "Token Authorization Changed") // Token授权已变更 + ErrTokenAuthExpire = NewError(1308, "Token Authorization Expire") // Token授权已过期 + ErrTokenJsonDecode = NewError(1309, "Token Authorization JSON Decode Error") // Token JSON解码错误 + ErrTokenJsonEncode = NewError(1310, "Token Authorization JSON Encode Error") // Token JSON编码错误 + ErrTokenSecretKey = NewError(1311, "Token SecretKey Error") // Token密钥错误 + ErrTokenSecretKeyNotFound = NewError(1312, "Token SecretKey Not Found") // Token密钥未找到 + ErrTokenGenerate = NewError(1313, "Generate Token Fail") // 生成令牌失败 + ErrTokenParse = NewError(1314, "Parse Token Fail") // 解析令牌失败 ) // 基础设施相关错误码,起始码:1500 @@ -98,6 +100,15 @@ func ErrFatal(code int, msg string) error { // code: 错误码 // msg: 错误消息,会自动转换为大写 func ErrNotFound(code int, msg string) error { - AllErrors[code] = strings.ToUpper(msg) - return status.New(codes.Code(code), strings.ToUpper(msg)).Err() + AllErrors[code] = msg + return status.New(codes.Code(code), msg).Err() +} + +// IsErr 检查错误是否与指定的错误匹配 +func IsErr(err, target error) bool { + return status.Code(err) == status.Code(target) +} + +func String(err error, msg string) error { + return status.New(status.Code(err), err.Error()+", "+msg).Err() } diff --git a/middleware/jwt.go b/middleware/jwt.go index b8fbd03..00201a2 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -60,8 +60,8 @@ func JwtAuth(time_verify bool) gin.HandlerFunc { func ParseAuth(c *gin.Context) (*types.JwtClaims, error) { claims, ok := c.Get("Auth") if !ok { - log.Printf("获取登录信息异常: %v", errcode.ErrJWTAuthNotFound) - return nil, errcode.ErrJWTAuthNotFound + log.Printf("获取登录信息异常: %v", errcode.ErrTokenAuthNotFound) + return nil, errcode.ErrTokenAuthNotFound } json_claims, err := json.Marshal(claims) diff --git a/service/meta.go b/service/meta.go index 3a3abca..8f7e1c3 100644 --- a/service/meta.go +++ b/service/meta.go @@ -20,12 +20,12 @@ func ParseMetaCtx(ctx context.Context, opts *ParseOptions) (*types.JwtClaims, er // 解析metada中的信息并验证 md, ok := metadata.FromIncomingContext(ctx) if !ok { - return nil, errcode.ErrJWTAuthNotFound + return nil, errcode.ErrTokenAuthNotFound } var Authorizations []string = md.Get("authorization") if len(Authorizations) == 0 || Authorizations[0] == "" { - return nil, errcode.ErrJWTAuthNotFound + return nil, errcode.ErrTokenAuthNotFound } claims, err := encipher.ParseTokenAes(Authorizations[0])