登录
首页 >  Golang >  Go教程

go GCM gin中间件的加密解密文件流处理

来源:脚本之家

时间:2022-12-31 10:00:42 351浏览 收藏

IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《go GCM gin中间件的加密解密文件流处理》,聊聊Gin、goGCM、加解密、文件流处理,我们一起来看看吧!

aes的gcm模式的加密和解密

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

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

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

package aes
import (
	"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) 

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

package middleware
import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/valyala/fasthttp"
	"io/ioutil"
	"runtime/debug"
	"time"
)
var fastClient *fasthttp.Client
func 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 middleware
import (
	"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)
}
//处理json
func 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) 

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

package main
import (
	"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

最后来看一下文件上传:

下载地址 :https://github.com/dz45693/gindemo

理论要掌握,实操不能落!以上关于《go GCM gin中间件的加密解密文件流处理》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!

声明:本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表