feat: 接口加解密中间件
This commit is contained in:
@@ -8,8 +8,13 @@ const (
|
|||||||
// 响应-msg错误失败
|
// 响应-msg错误失败
|
||||||
MSG_ERROR = "error"
|
MSG_ERROR = "error"
|
||||||
|
|
||||||
// 响应-msg正常成功
|
|
||||||
CODE_SUCCESS = 1
|
|
||||||
// 响应-code正常成功
|
// 响应-code正常成功
|
||||||
|
CODE_SUCCESS = 1
|
||||||
|
// 响应-msg正常成功
|
||||||
MSG_SUCCESS = "success"
|
MSG_SUCCESS = "success"
|
||||||
|
|
||||||
|
// 响应-code加密数据
|
||||||
|
CODE_ENCRYPT = 2
|
||||||
|
// 响应-msg加密数据
|
||||||
|
MSG_ENCRYPT = "encrypt"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ func ErrorCatch() gin.HandlerFunc {
|
|||||||
|
|
||||||
// 返回错误响应给客户端
|
// 返回错误响应给客户端
|
||||||
if config.Env() == "prod" {
|
if config.Env() == "prod" {
|
||||||
c.JSON(500, result.ErrMsg("Internal Server Errors"))
|
c.JSON(500, result.CodeMsg(500, "Internal Server Errors"))
|
||||||
} else {
|
} else {
|
||||||
// 通过实现 error 接口的 Error() 方法自定义错误类型进行捕获
|
// 通过实现 error 接口的 Error() 方法自定义错误类型进行捕获
|
||||||
switch v := err.(type) {
|
switch v := err.(type) {
|
||||||
case error:
|
case error:
|
||||||
c.JSON(500, result.ErrMsg(v.Error()))
|
c.JSON(500, result.CodeMsg(500, v.Error()))
|
||||||
default:
|
default:
|
||||||
c.JSON(500, result.ErrMsg(fmt.Sprint(err)))
|
c.JSON(500, result.CodeMsg(500, fmt.Sprint(err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
152
src/framework/middleware/crypto_api.go
Normal file
152
src/framework/middleware/crypto_api.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"nms_cxy/src/framework/config"
|
||||||
|
constResult "nms_cxy/src/framework/constants/result"
|
||||||
|
"nms_cxy/src/framework/logger"
|
||||||
|
"nms_cxy/src/framework/utils/crypto"
|
||||||
|
"nms_cxy/src/framework/utils/parse"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CryptoApi 接口加解密
|
||||||
|
//
|
||||||
|
// 示例参数:middleware.CryptoApi(true, true)
|
||||||
|
//
|
||||||
|
// 参数表示:对请求解密,对响应加密
|
||||||
|
//
|
||||||
|
// 请将中间件放在最前置,对请求优先处理
|
||||||
|
func CryptoApi(requestDecrypt, responseEncrypt bool) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 请求解密时对请求data注入
|
||||||
|
if requestDecrypt {
|
||||||
|
method := c.Request.Method
|
||||||
|
contentType := c.ContentType()
|
||||||
|
contentDe := ""
|
||||||
|
// 请求参数解析
|
||||||
|
if method == "GET" {
|
||||||
|
contentDe = c.Query("data")
|
||||||
|
} else if contentType == gin.MIMEJSON {
|
||||||
|
var body struct {
|
||||||
|
Data string `json:"data" binding:"required"`
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindJSON(&body); err == nil {
|
||||||
|
contentDe = body.Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否存在data字段数据
|
||||||
|
if contentDe == "" {
|
||||||
|
c.JSON(400, map[string]any{
|
||||||
|
"code": constResult.CODE_ERROR,
|
||||||
|
"msg": "decrypt not found field data",
|
||||||
|
})
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密-原数据加密前含16位长度iv
|
||||||
|
apiKey := config.Get("aes.apiKey").(string)
|
||||||
|
dataBodyStr, err := crypto.AESDecryptBase64(contentDe, apiKey)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("CryptoApi decrypt err => %v", err)
|
||||||
|
c.JSON(400, map[string]any{
|
||||||
|
"code": constResult.CODE_ERROR,
|
||||||
|
"msg": "decrypted data could not be parsed",
|
||||||
|
})
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配回请求体
|
||||||
|
if method == "GET" {
|
||||||
|
var urlParams map[string]any
|
||||||
|
json.Unmarshal([]byte(dataBodyStr), &urlParams)
|
||||||
|
rawQuery := []string{}
|
||||||
|
for k, v := range urlParams {
|
||||||
|
rawQuery = append(rawQuery, fmt.Sprintf("%s=%v", k, v))
|
||||||
|
}
|
||||||
|
c.Request.URL.RawQuery = strings.Join(rawQuery, "&")
|
||||||
|
} else if contentType == gin.MIMEJSON {
|
||||||
|
c.Request.Body = io.NopCloser(bytes.NewBuffer([]byte(dataBodyStr)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应加密时替换原有的响应体
|
||||||
|
var rbw *replaceBodyWriter
|
||||||
|
if responseEncrypt {
|
||||||
|
rbw = &replaceBodyWriter{
|
||||||
|
body: &bytes.Buffer{},
|
||||||
|
ResponseWriter: c.Writer,
|
||||||
|
}
|
||||||
|
c.Writer = rbw
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
// 响应加密时对响应data数据进行加密
|
||||||
|
if responseEncrypt {
|
||||||
|
// 满足成功并带数据的响应进行加密
|
||||||
|
if c.Writer.Status() == 200 {
|
||||||
|
var resBody map[string]any
|
||||||
|
json.Unmarshal(rbw.body.Bytes(), &resBody)
|
||||||
|
codeV, codeOk := resBody["code"]
|
||||||
|
dataV, dataOk := resBody["data"]
|
||||||
|
if codeOk && dataOk {
|
||||||
|
if parse.Number(codeV) == constResult.CODE_SUCCESS {
|
||||||
|
byteBodyData, _ := json.Marshal(dataV)
|
||||||
|
// 加密-原数据头加入标记16位长度iv终止符
|
||||||
|
apiKey := config.Get("aes.apiKey").(string)
|
||||||
|
contentEn, err := crypto.AESEncryptBase64("=:)"+string(byteBodyData), apiKey)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("CryptoApi encrypt err => %v", err)
|
||||||
|
rbw.ReplaceWrite([]byte(fmt.Sprintf(`{"code":"%d","msg":"encrypt err"}`, constResult.CODE_ERROR)))
|
||||||
|
} else {
|
||||||
|
// 响应加密
|
||||||
|
byteBody, _ := json.Marshal(map[string]any{
|
||||||
|
"code": constResult.CODE_ENCRYPT,
|
||||||
|
"msg": constResult.MSG_ENCRYPT,
|
||||||
|
"data": contentEn,
|
||||||
|
})
|
||||||
|
rbw.ReplaceWrite(byteBody)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rbw.ReplaceWrite(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rbw.ReplaceWrite(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceBodyWriter 替换默认的响应体
|
||||||
|
type replaceBodyWriter struct {
|
||||||
|
gin.ResponseWriter
|
||||||
|
body *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 写入响应体
|
||||||
|
func (r replaceBodyWriter) Write(b []byte) (int, error) {
|
||||||
|
return r.body.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWrite 替换响应体
|
||||||
|
func (r *replaceBodyWriter) ReplaceWrite(b []byte) (int, error) {
|
||||||
|
if b == nil {
|
||||||
|
return r.ResponseWriter.Write(r.body.Bytes())
|
||||||
|
}
|
||||||
|
r.body = &bytes.Buffer{}
|
||||||
|
r.body.Write(b)
|
||||||
|
return r.ResponseWriter.Write(r.body.Bytes())
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nms_cxy/src/framework/logger"
|
"nms_cxy/src/framework/logger"
|
||||||
@@ -18,6 +19,10 @@ func Report() gin.HandlerFunc {
|
|||||||
|
|
||||||
// 计算请求处理时间,并打印日志
|
// 计算请求处理时间,并打印日志
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
logger.Infof("%s %s report end=> %v", c.Request.Method, c.Request.RequestURI, duration)
|
// logger.Infof("%s %s report end=> %v", c.Request.Method, c.Request.RequestURI, duration)
|
||||||
|
// 获取当前活跃的goroutine数量
|
||||||
|
num := runtime.NumGoroutine()
|
||||||
|
// logger.Infof("当前活跃的goroutine数量 %d\n", num)
|
||||||
|
logger.Infof("\n访问接口 %s %s\n总耗时 %v\n当前活跃的goroutine数量 %d\n", c.Request.Method, c.Request.RequestURI, duration, num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StringEncryptByAES 字符串AES加密
|
// AESEncryptBase64 AES加密转Base64字符串
|
||||||
func StringEncryptByAES(text string) (string, error) {
|
func AESEncryptBase64(text, key string) (string, error) {
|
||||||
if len(text) == 0 {
|
if len(text) == 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
xpass, err := aesEncryptWithSalt([]byte(text))
|
xpass, err := AESEncrypt([]byte(text), []byte(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,8 @@ func StringEncryptByAES(text string) (string, error) {
|
|||||||
return pass64, nil
|
return pass64, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringDecryptByAES 字符串AES解密
|
// AESDecryptBase64 AES解密解Base64字符串
|
||||||
func StringDecryptByAES(text string) (string, error) {
|
func AESDecryptBase64(text, key string) (string, error) {
|
||||||
if len(text) == 0 {
|
if len(text) == 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@@ -32,21 +32,16 @@ func StringDecryptByAES(text string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
tpass, err := AESDecrypt(bytesPass, []byte(key))
|
||||||
tpass, err := aesDecryptWithSalt(bytesPass)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return string(tpass), nil
|
return string(tpass), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// aesKey 字符串AES加解密密钥
|
// AESEncrypt AES加密
|
||||||
const aesKey = "AGT66VfY4SMaiT97a7df0aef1704d5c5"
|
func AESEncrypt(plaintext, aeskey []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(aeskey)
|
||||||
// const aesKey = "AGT66VfY4SMaiT97"
|
|
||||||
// aesEncryptWithSalt AES加密
|
|
||||||
func aesEncryptWithSalt(plaintext []byte) ([]byte, error) {
|
|
||||||
block, err := aes.NewCipher([]byte(aesKey))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -68,8 +63,8 @@ func aesEncryptWithSalt(plaintext []byte) ([]byte, error) {
|
|||||||
return ciphertext, nil
|
return ciphertext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// aesDecryptWithSalt AES解密
|
// AESDecrypt AES解密
|
||||||
func aesDecryptWithSalt(ciphertext []byte) ([]byte, error) {
|
func AESDecrypt(ciphertext, aeskey []byte) ([]byte, error) {
|
||||||
blockSize := aes.BlockSize
|
blockSize := aes.BlockSize
|
||||||
if len(ciphertext) < blockSize {
|
if len(ciphertext) < blockSize {
|
||||||
return nil, fmt.Errorf("ciphertext too short")
|
return nil, fmt.Errorf("ciphertext too short")
|
||||||
@@ -77,12 +72,14 @@ func aesDecryptWithSalt(ciphertext []byte) ([]byte, error) {
|
|||||||
|
|
||||||
iv := ciphertext[:blockSize]
|
iv := ciphertext[:blockSize]
|
||||||
ciphertext = ciphertext[blockSize:]
|
ciphertext = ciphertext[blockSize:]
|
||||||
|
block, err := aes.NewCipher([]byte(aeskey))
|
||||||
|
|
||||||
block, err := aes.NewCipher([]byte(aesKey))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(ciphertext) == 0 {
|
||||||
|
return nil, fmt.Errorf("ciphertext is invalid")
|
||||||
|
}
|
||||||
if len(ciphertext)%blockSize != 0 {
|
if len(ciphertext)%blockSize != 0 {
|
||||||
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
|
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user