千家信息网

go GCM gin中间件怎么加密解密文件

发表于:2024-11-18 作者:千家信息网编辑
千家信息网最后更新 2024年11月18日,这篇文章主要介绍"go GCM gin中间件怎么加密解密文件"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"go GCM gin中间件怎么加密解密文件"文章能帮
千家信息网最后更新 2024年11月18日go GCM gin中间件怎么加密解密文件

这篇文章主要介绍"go GCM gin中间件怎么加密解密文件"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"go GCM gin中间件怎么加密解密文件"文章能帮助大家解决问题。

aes的gcm模式的加密和解密

要给已有的系统启用加密解密,目前推荐的是aes的gcm模式的加密和解密,在微服务如果向前有公共方法处理 读取数据和写返回数据,那么比较简单,修改以前的公共方法,但是这样本地调试平时肯定是明文,所以要加判断,如果以前的读数据和写数据是五花八门那就比较麻烦,在微服务体系里面一般有网关这个服务,所以加密和解密就放在网关服务,大致如下:

常规的请求有GET,POST JSON, POST file,以及POST Form表单,返回一般是json 或者下载文件流,所以我们需要截获请求流和返回流,收到请求流解密数据 然后重新写入到请求流,收到返回流加密数据,重写返回流。

首先来看aes加密和解密程序aes.go

package aesimport (        "crypto/aes"        "crypto/cipher"        "crypto/md5"        "crypto/rand"        "encoding/base64"        "encoding/hex"        "errors"        "io")//加密字符串func GcmEncrypt(key, plaintext string) (string, error) {        if len(key) != 32 && len(key) != 24 && len(key) != 16 {                return "", errors.New("the length of key is error")        }        if len(plaintext) < 1 {                return "", errors.New("plaintext is null")        }        keyByte := []byte(key)        plainByte:=[]byte(plaintext)        block, err := aes.NewCipher(keyByte)        if err != nil {                return "", err        }        aesGcm, err := cipher.NewGCM(block)        if err != nil {                return "", err        }        nonce := make([]byte, 12)        if _, err := io.ReadFull(rand.Reader, nonce); err != nil {                return "", err        }        seal := aesGcm.Seal(nonce, nonce, plainByte, nil)        return base64.URLEncoding.EncodeToString(seal), nil}//解密字符串func GcmDecrypt(key, cipherText string) (string, error) {        if len(key) != 32 && len(key) != 24 && len(key) != 16 {                return "", errors.New("the length of key is error")        }        if len(cipherText) < 1 {                return "", errors.New("cipherText is null")        }        cipherByte, err := base64.URLEncoding.DecodeString(cipherText)        if err != nil {                return "", err        }        if len(cipherByte) < 12 {                return "", errors.New("cipherByte is error")        }        nonce, cipherByte := cipherByte[:12], cipherByte[12:]        keyByte := []byte(key)        block, err := aes.NewCipher(keyByte)        if err != nil {                return "", err        }        aesGcm, err := cipher.NewGCM(block)        if err != nil {                return "", err        }        plainByte, err := aesGcm.Open(nil, nonce, cipherByte, nil)        if err != nil {                return "", err        }        return string(plainByte), nil}//生成32位md5字串func GetAesKey(s string) string {                h := md5.New()                h.Write([]byte(s))                return hex.EncodeToString(h.Sum(nil))}

再来看看网关转发程序proxy.go

package middlewareimport (        "fmt"        "github.com/gin-gonic/gin"        "github.com/valyala/fasthttp"        "io/ioutil"        "runtime/debug"        "time")var fastClient *fasthttp.Clientfunc init() {        fastClient = &fasthttp.Client{}        fastClient.MaxIdemponentCallAttempts = 1        fastClient.ReadTimeout = time.Second * 60}func GetHttpClient() *fasthttp.Client {        return fastClient}func GateWay() gin.HandlerFunc {        return func(c *gin.Context) {                defer func() {                        if e := recover(); e != nil {                                stack := debug.Stack()                                log("GateWay Recovery: err:%v, stack:%v", e, string(stack))                        }                }()                err := Forward(c)                if err != nil {                        response(c, 9999, "系统错误", err.Error())                }                return        }}func Forward(ctx *gin.Context) error {        req := &fasthttp.Request{}        //请求-获取服务地址        host := "http://localhost:8000/" + ctx.Request.URL.String()        //请求-url        req.SetRequestURI(host)        //请求-header        for k, v := range ctx.Request.Header {                req.Header.Set(k, v[0])        }        //请求-body        data, err := ioutil.ReadAll(ctx.Request.Body)        if err != nil {                log("Forward err:%v", err)                return fmt.Errorf("系统错误")        }        req.SetBody(data)        //请求-方法        req.Header.SetMethod(ctx.Request.Method)        //请求-发送        resp := &fasthttp.Response{}        //请求-新增调用链        /*                err = opentracing.GlobalTracer().Inject(                        opentracing.SpanFromContext(ctx.Request.Context()).Context(),                        opentracing.TextMap,                        HTTPHeadersCarrier{&req.Header},                )        */        err = GetHttpClient().Do(req, resp)        if err != nil {                log("Forward GetHttpClient DO err:%v", err)                return fmt.Errorf("系统错误")        }        //请求-响应        ContentType := fmt.Sprintf("%s", resp.Header.Peek("Content-Type"))        ctx.Data(resp.StatusCode(), ContentType, resp.Body())        return nil}type HTTPHeadersCarrier struct {        *fasthttp.RequestHeader}func (c HTTPHeadersCarrier) Set(key, val string) {        h := c.RequestHeader        h.Add(key, val)}

最后来看一下gin的中间件crypto.go

package middlewareimport (        "bytes"        "demo/aes"        "encoding/json"        "errors"        "fmt"        "github.com/gin-gonic/gin"        "io"        "io/ioutil"        "mime"        "mime/multipart"        "net/url"        "runtime/debug"        "strconv"        "strings")type aesWriter struct {        gin.ResponseWriter        body *bytes.Buffer}func (w *aesWriter) Write(b []byte) (int, error) {        return w.body.Write(b)}func (w *aesWriter) WriteString(s string) (int, error) {        return w.body.WriteString(s)}//只有经过token 验证的才会加密 和解密//handleFile 表示是否处理上传文件, 默认网关不处理上传文件的encryptString数据, 如果处理会导致具体服务无法接收到具体参数func AesGcmDecrypt() gin.HandlerFunc {        return func(c *gin.Context) {                defer func() {                        if e := recover(); e != nil {                                stack := debug.Stack()                                log("AesGcmDecrypt Recovery: err:%v, stack:%v", e, string(stack))                        }                }()                if c.Request.Method == "OPTIONS" {                        c.Next()                } else {                        md5key := aes.GetAesKey("gavin12345678")                        log("AesGcmDecrypt start url:%s  ,md5key:%s, Method:%s, Header:%+v", c.Request.URL.String(), md5key, c.Request.Method, c.Request.Header)                        handleAes(c, md5key)                }        }}//请求和返回都加密 解密func handleAes(c *gin.Context, md5key string) {        contentType := c.Request.Header.Get("Content-Type")        isJsonRequest := strings.Contains(contentType, "application/json")        isFileRequest := strings.Contains(contentType, "multipart/form-data")        isFormUrl := strings.Contains(contentType, "application/x-www-form-urlencoded")        if c.Request.Method == "GET" {                err := parseQuery(c, md5key)                if err != nil {                        log("handleAes parseQuery  err:%v", err)                        //这里输出应该密文 一旦加密解密调试好 这里就不会走进来                        response(c, 2001, "系统错误", err.Error())                        return                }        } else if isJsonRequest {                err := parseJson(c, md5key)                if err != nil {                        log("handleAes parseJson err:%v", err)                        //这里输出应该密文 一旦加密解密调试好 这里就不会走进来                        response(c, 2001, "系统错误", err.Error())                        return                }        } else if isFormUrl {                err := parseForm(c, md5key)                if err != nil {                        log("handleAes parseForm err:%v", err)                        //这里输出应该密文 一旦加密解密调试好 这里就不会走进来                        response(c, 2001, "系统错误", err.Error())                        return                }        } else if isFileRequest {                err := parseFile(c, md5key)                if err != nil {                        log("handleAes parseFile err:%v", err)                        //这里输出应该密文 一旦加密解密调试好 这里就不会走进来                        response(c, 2001, "系统错误", err.Error())                        return                }        }        ///截取 response body        oldWriter := c.Writer        blw := &aesWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}        c.Writer = blw        // 走流程        c.Next()        ///获取返回数据        responseByte := blw.body.Bytes()        //日志        c.Writer = oldWriter        //如果返回的不是json格式 那么直接返回,应为文件下载之类的不应该加密        if !isJsonResponse(c) {                _, _ = c.Writer.Write(responseByte)                return        }        ///加密        encryptStr, err := aes.GcmEncrypt(md5key, string(responseByte))        if err != nil {                log("handleAes GcmEncrypt err:%v", err)                response(c, 2001, "系统错误", err.Error())                return        }        _, _ = c.Writer.WriteString(encryptStr)}//处理jsonfunc parseJson(c *gin.Context, md5key string) error {        //读取数据 body处理        payload, err := c.GetRawData()        if err != nil {                return err        }        ///解密body数据 请求的json是{"encryptString":{value}} value含有gcm的12字节nonce,实际长度大于32        if payload != nil && len(payload) > 20 {                var jsonData encryptJson                log("AesGcmDecrypt  parseJson url:%s md5key:%s,old data:%s,", c.Request.URL.String(), md5key, string(payload))                err := json.Unmarshal(payload, &jsonData)                if err != nil {                        log("AesGcmDecrypt parseJson Unmarshal err:%v", err)                        return err                }                payloadText := jsonData.EncryptString                if len(payloadText) > 0 {                        payloadText, err = aes.GcmDecrypt(md5key, payloadText)                        if err != nil {                                log("AesGcmDecrypt parseJson GcmDecryptByte err:%v", err)                                return err                        }                        payload = []byte(payloadText)                        log("AesGcmDecrypt  parseJson url:%s md5key:%s,encryptString:%s,decrypt data:%s", c.Request.URL.String(), md5key, jsonData.EncryptString, payloadText)                }        }        c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(payload))        return nil}func parseForm(c *gin.Context, md5key string) error {        //读取数据 body处理        payload, err := c.GetRawData()        if err != nil {                return err        }        ///解密body数据 请求的json是"encryptString= value含有gcm的12字节nonce,实际长度大于32        if payload != nil && len(payload) > 20 {                var jsonData encryptJson                log("AesGcmDecrypt  parseForm url:%s md5key:%s,old data:%s,", c.Request.URL.String(), md5key, string(payload))                values, err := url.ParseQuery(string(payload))                if err != nil {                        log("AesGcmDecrypt parseForm ParseQuery err:%v", err)                        return err                }                payloadText := values.Get("encryptString")                if len(payloadText) > 0 {                        mapData, err := gcmDecryptString(md5key, payloadText)                        if err != nil {                                log("AesGcmDecrypt parseForm gcmDecryptString err:%v", err)                                return err                        }                        for k, v := range mapData {                                values.Add(k, getStr(v))                        }                        formData := values.Encode()                        log("AesGcmDecrypt  parseForm url:%s md5key:%s,encryptString:%s,decrypt data:%s", c.Request.URL.String(), md5key, jsonData.EncryptString, formData)                        payload = []byte(formData)                }        }        c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(payload))        return nil}//处理get url的解密func parseQuery(c *gin.Context, md5Key string) error {        encryptString := c.Query("encryptString")        log("AesGcmDecrypt parseQuery url:%s, md5key:%s, encryptString:%s", c.Request.URL.String(), md5Key, encryptString)        if len(encryptString) < 1 {                return nil        }        queryData, err := gcmDecryptString(md5Key, encryptString)        if err != nil {                return err        }        var args []string        var logs []string        for k, v := range queryData {                val := getStr(v)                args = append(args, fmt.Sprintf("%s=%s", k, url.QueryEscape(val)))                logs = append(logs, fmt.Sprintf("%s=%s", k, val))        }        log("AesGcmDecrypt parseQuery  url:%s, md5key:%s, encryptString:%s, decrypt data:%s", c.Request.URL.String(), md5Key, encryptString, strings.Join(logs, "&"))        c.Request.URL.RawQuery = strings.Join(args, "&")        return nil}func parseFile(c *gin.Context, md5Key string) error {        contentType := c.Request.Header.Get("Content-Type")        _, params, _ := mime.ParseMediaType(contentType)        boundary, ok := params["boundary"]        if !ok {                return errors.New("no multipart boundary param in Content-Type")        }        //准备重写数据        bodyBuf := &bytes.Buffer{}        wr := multipart.NewWriter(bodyBuf)        mr := multipart.NewReader(c.Request.Body, boundary)        for {                p, err := mr.NextPart() //p的类型为Part                if err == io.EOF {                        break                }                if err != nil {                        log("NextPart err:%v", err)                        break                }                fileByte, err := ioutil.ReadAll(p)                if err != nil {                        log("ReadAll err:%v", err)                        break                }                pName := p.FormName()                fileName := p.FileName()                if len(fileName) < 1 {                        if pName == "encryptString" {                                formData, err := gcmDecryptString(md5Key, string(fileByte))                                if err != nil {                                        log("AesGcmDecrypt writeFile gcmDecryptString err:%v", err)                                        break                                }                                for k, v := range formData {                                        val := getStr(v)                                        err = wr.WriteField(k, val)                                        if err != nil {                                                log("AesGcmDecrypt writeFile WriteField :%s=%s, err:%v", k, val, err)                                                break                                        }                                }                        } else {                                wr.WriteField(pName, string(fileByte))                        }                } else {                        tmp, err := wr.CreateFormFile(pName, fileName)                        if err != nil {                                log("AesGcmDecrypt parseFile CreateFormFile err:%v", err)                                continue                        }                        tmp.Write(fileByte)                }        }        //写结尾标志        _ = wr.Close()        c.Request.Header.Set("Content-Type", wr.FormDataContentType())        c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBuf.Bytes()))        return nil}func gcmDecryptString(md5Key, encryptString string) (map[string]interface{}, error) {        formData := make(map[string]interface{}, 0)        if len(encryptString) < 1 {                return formData, nil        }        plaintext, err := aes.GcmDecrypt(md5Key, encryptString)        if err != nil {                return formData, err        }        if len(plaintext) < 3 {                //plaintext 应该是json 串 {}                return formData, nil        }        err = json.Unmarshal([]byte(plaintext), &formData)        if err != nil {                return formData, err        }        return formData, nil}func isJsonResponse(c *gin.Context) bool {        contentType := c.Writer.Header().Get("Content-Type")        return strings.Contains(contentType, "application/json")}func getStr(v interface{}) string {        val := ""        switch v.(type) {        case float64:                //tmp, _ := decimal.NewFromString(fmt.Sprintf("%.10f", v))                fl, _ := v.(float64)                val = strconv.FormatFloat(fl, 'f', -1, 64)        default:                val = fmt.Sprintf("%v", v)        }        return val}type encryptJson struct {        EncryptString string `json:"encryptString"`}func log(format string, arg ...interface{}) {        fmt.Print(fmt.Sprintf(format, arg...))}func response(c *gin.Context, code int, msg string, data interface{}) {        mapData := make(map[string]interface{}, 0)        mapData["code"] = code        mapData["msg"] = msg        mapData["data"] = data        c.JSON(200, data)        c.Abort()        return}

最后我们来写一个demo程序main.go

package mainimport (        "demo/middleware"        "fmt"        "github.com/gin-gonic/gin"        "os")func main() {        go func() {                gateway := gin.Default()                gateway.Use(middleware.AesGcmDecrypt())                gateway.Use(middleware.GateWay())                gateway.Run(":8080")        }()        // 1.创建路由        r := gin.Default()        r.Use(middleware.Logger())        r.GET("/", func(c *gin.Context) {                c.Writer.WriteString("pong")        })        r.GET("/demo", func(c *gin.Context) {                req := ReqObj{}                err := c.ShouldBindQuery(&req)                if err != nil {                        fmt.Print(err)                }                response(c, 200, "ok", req)        })        r.POST("/test", func(c *gin.Context) {                req := ReqObj{}                err := c.ShouldBind(&req)                if err != nil {                        fmt.Print(err)                }                response(c, 200, "ok", req)        })        r.POST("/form", func(c *gin.Context) {                req := ReqObj{}                err := c.ShouldBind(&req)                if err != nil {                        fmt.Print(err)                }                response(c, 200, "ok", req)        })        r.POST("/upload", func(c *gin.Context) {                file, err := c.FormFile("file")                if err != nil {                        fmt.Print(err)                }                folder := c.Request.FormValue("folder")                tmp, _ := os.Getwd()                filePath := tmp + "/upload/" + folder + "/" + file.Filename                c.SaveUploadedFile(file, filePath)        })        r.Run(":8000")}type ReqObj struct {        Name       string `json:"name" form:"name"`        Age        int64  `json:"age"  form:"age"`        UpdateTime int64  `json:"update_time"  form:"update_time"`        Folder     string `json:"folder"  form:"folder"`}func response(c *gin.Context, code int, msg string, data interface{}) {        mapData := make(map[string]interface{}, 0)        mapData["code"] = code        mapData["msg"] = msg        mapData["data"] = data        c.JSON(200, data)        c.Abort()        return}

验证

来让我们一次验证一下运行结果:

1.GET请求

2.看看post json

3验证postform

最后来看一下文件上传:

关于"go GCM gin中间件怎么加密解密文件"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注行业资讯频道,小编每天都会为大家更新不同的知识点。

0