diff --git a/app.log b/app.log deleted file mode 100644 index e69de29..0000000 diff --git a/go.mod b/go.mod index 1be180e..33270c5 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( git.diulo.com/mogfee/go-common v0.0.0-20230301071202-cc79129d6a24 github.com/PuerkitoBio/goquery v1.8.1 + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/envoyproxy/protoc-gen-validate v0.1.0 github.com/gin-gonic/gin v1.9.0 github.com/go-playground/form v3.1.4+incompatible diff --git a/go.sum b/go.sum index fa4d714..a9a882b 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= diff --git a/pkg/jwts/jwts.go b/pkg/jwts/jwts.go new file mode 100644 index 0000000..f040b77 --- /dev/null +++ b/pkg/jwts/jwts.go @@ -0,0 +1,70 @@ +package jwts + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + "github.com/dgrijalva/jwt-go" + "github.com/pkg/errors" + "math/big" +) + +var ( + //ES256 keys + ECDSAKeyD = "7A429E82FF619D38CC8071111988FFA75625DD83B22E9EBEC29F17BFA7BF3A03" + ECDSAKeyX = "76E93569AB21A614BCD581858D0066C8ED611DEFEEA2821CC43EC9E08948A151" + ECDSAKeyY = "61BB8B7EF5333E2E87CDE6DF522BE6BF253C637768F9FA8D9EDCAB270E09B43C" +) + +// 获取token数据 +func JWTGetMapString(map1 jwt.Claims) (string, error) { + + keyD := new(big.Int) + keyX := new(big.Int) + keyY := new(big.Int) + keyD.SetString(ECDSAKeyD, 16) + keyX.SetString(ECDSAKeyX, 16) + keyY.SetString(ECDSAKeyY, 16) + + publicKey := ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: keyX, + Y: keyY, + } + privateKey := ecdsa.PrivateKey{D: keyD, PublicKey: publicKey} + + tokenJwt := jwt.NewWithClaims(jwt.SigningMethodES256, map1) + + if tokenString, err := tokenJwt.SignedString(&privateKey); err != nil { + return "", err + } else { + return tokenString, nil + } +} + +// 解析jwt数据 +func JWTGetStringMap(jwtString string) (map[string]interface{}, error) { + keyX := new(big.Int) + keyY := new(big.Int) + + keyX.SetString(ECDSAKeyX, 16) + keyY.SetString(ECDSAKeyY, 16) + publickKey := ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: keyX, + Y: keyY, + } + jwtToken, err := jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok { + return nil, errors.New(fmt.Sprintf("json 解析失败:%+v", token)) + } + return &publickKey, nil + }) + + if err == nil { + if claims, ok := jwtToken.Claims.(jwt.MapClaims); ok { + return claims, nil + } + } + return nil, errors.New(fmt.Sprintf("json 解析失败:%v", jwtString)) +} diff --git a/pkg/xaes/xaes.go b/pkg/xaes/xaes.go new file mode 100644 index 0000000..ec34576 --- /dev/null +++ b/pkg/xaes/xaes.go @@ -0,0 +1,104 @@ +package xaes + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" +) + +//#;AES加解密使用 长度 16, 24, 32 +//const aesKey = "sfe023f_9fd&fwfl" + +////加密字符串 +//func AesEncrypt(hstring string) (string, error) { +// if result, err := aesEncrypt([]byte(hstring), []byte(aesKey)); err != nil { +// return "", err +// } else { +// return base64.StdEncoding.EncodeToString(result), nil +// } +//} +// +////解密字符串 +//func AesDecrypt(hstring string) (string, error) { +// if bye, err := base64.StdEncoding.DecodeString(hstring); err != nil { +// return "", err +// } else { +// if result, err := aesDecrypt(bye, []byte(aesKey)); err != nil { +// return "", err +// } else { +// return string(result), nil +// } +// } +//} +//func AESTestCode() { +// // AES-128。key长度:16, 24, 32 bytes 对应 AES-128, AES-192, AES-256 +// key := []byte("sfe023f_9fd&fwfl") +// result, err := aesEncrypt([]byte("polaris@studygolang"), key) +// if err != nil { +// panic(err) +// } +// fmt.Println(result) +// a, _ := AesEncrypt("polaris@studygolang") +// aa, _ := base64.StdEncoding.DecodeString(a) +// origData, err := aesDecrypt(aa, key) +// if err != nil { +// panic(err) +// } +// fmt.Println(string(origData)) +//} + +func Encrypt(origData, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + origData = pKCS5Padding(origData, blockSize) + // origData = ZeroPadding(origData, block.BlockSize()) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + crypted := make([]byte, len(origData)) + // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以 + // crypted := origData + blockMode.CryptBlocks(crypted, origData) + return crypted, nil +} + +func Decrypt(crypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(crypted)) + // origData := crypted + + blockMode.CryptBlocks(origData, crypted) + + origData = pKCS5UnPadding(origData) + // origData = ZeroUnPadding(origData) + return origData, nil +} + +// func zeroPadding(ciphertext []byte, blockSize int) []byte { +// padding := blockSize - len(ciphertext)%blockSize +// padtext := bytes.Repeat([]byte{0}, padding) +// return append(ciphertext, padtext...) +// } +// +// func zeroUnPadding(origData []byte) []byte { +// length := len(origData) +// unpadding := int(origData[length-1]) +// return origData[:(length - unpadding)] +// } +func pKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +func pKCS5UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} diff --git a/pkg/xbase64/xbase64.go b/pkg/xbase64/xbase64.go new file mode 100644 index 0000000..fcee88a --- /dev/null +++ b/pkg/xbase64/xbase64.go @@ -0,0 +1,14 @@ +package xbase64 + +import ( + b64 "encoding/base64" +) + +func Encode(s string) string { + return b64.StdEncoding.EncodeToString([]byte(s)) +} + +func Decode(s string) (string, error) { + ds, err := b64.StdEncoding.DecodeString(s) + return string(ds), err +} diff --git a/pkg/xtime/xtoken/xtoken.go b/pkg/xtime/xtoken/xtoken.go new file mode 100644 index 0000000..84bc5a3 --- /dev/null +++ b/pkg/xtime/xtoken/xtoken.go @@ -0,0 +1,85 @@ +package xtoken + +import ( + "context" + "encoding/json" + "errors" + "git.diulo.com/mogfee/protoc-gen-kit/pkg/jwts" + "git.diulo.com/mogfee/protoc-gen-kit/pkg/xaes" + "git.diulo.com/mogfee/protoc-gen-kit/pkg/xbase64" + "github.com/dgrijalva/jwt-go" + "time" +) + +const ( + // expiredTime 授权结束时间 + expiredTime = 7200 + + tokenKey = "68Tz&xWUW5U$Id45" +) + +type TokenInfo struct { + Id int64 + Permission []string +} + +var tokenExpired = errors.New("token expired") + +type tokenStoreInfo struct { + Created int64 `json:"created"` + ExpiredAt int64 `json:"expired_at"` + Id int64 `json:"id"` + Permission []string `json:"permission"` +} + +func GetTokenStr(ctx context.Context, info TokenInfo) (string, error) { + ctime := time.Now().Unix() + + tokenString, err := jwts.JWTGetMapString(jwt.MapClaims{ + "id": info.Id, + "permission": info.Permission, + "created": ctime, + "expired_at": ctime + expiredTime, + }) + + if err != nil { + return "", err + } + aesString, err := xaes.Encrypt([]byte(tokenString), []byte(tokenKey)) + if err != nil { + return "", err + } + return xbase64.Encode(string(aesString)), nil +} + +func ParseToken(tokenStr string) (*TokenInfo, error) { + tokenStr, err := xbase64.Decode(tokenStr) + if err != nil { + return nil, err + } + body, err := xaes.Decrypt([]byte(tokenStr), []byte(tokenKey)) + if err != nil { + return nil, err + } + mps, err := jwts.JWTGetStringMap(string(body)) + if err != nil { + return nil, errors.New("token parse error") + } + b, _ := json.Marshal(mps) + row := tokenStoreInfo{} + if err = json.Unmarshal(b, &row); err != nil { + return nil, err + } + + if row.ExpiredAt < time.Now().Unix() || row.Id <= 0 { + return nil, tokenExpired + } + + return &TokenInfo{ + Id: row.Id, + Permission: row.Permission, + }, nil +} +func IsExpired(err error) bool { + return err == tokenExpired +}