欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go?GCM?gin中間件的加密解密文件流處理

 更新時間:2022年05月19日 14:52:03   作者:dz45693  
這篇文章主要介紹了go語言?GCM加密解密,gin中間件的加密解密及文件流處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>

aes的gcm模式的加密和解密

要給已有的系統(tǒng)啟用加密解密,目前推薦的是aes的gcm模式的加密和解密,在微服務(wù)如果向前有公共方法處理 讀取數(shù)據(jù)和寫返回數(shù)據(jù),那么比較簡單,修改以前的公共方法,但是這樣本地調(diào)試平時肯定是明文,所以要加判斷,如果以前的讀數(shù)據(jù)和寫數(shù)據(jù)是五花八門那就比較麻煩,在微服務(wù)體系里面一般有網(wǎng)關(guān)這個服務(wù),所以加密和解密就放在網(wǎng)關(guān)服務(wù),大致如下:

常規(guī)的請求有GET,POST JSON, POST file,以及POST Form表單,返回一般是json 或者下載文件流,所以我們需要截獲請求流和返回流,收到請求流解密數(shù)據(jù) 然后重新寫入到請求流,收到返回流加密數(shù)據(jù),重寫返回流。

首先來看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) < 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))
}

再來看看網(wǎng)關(guān)轉(zhuǎn)發(fā)程序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, "系統(tǒng)錯誤", err.Error())
		}
		return
	}
}
func Forward(ctx *gin.Context) error {
	req := &fasthttp.Request{}
	//請求-獲取服務(wù)地址
	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("系統(tǒng)錯誤")
	}
	req.SetBody(data)
	//請求-方法
	req.Header.SetMethod(ctx.Request.Method)
	//請求-發(fā)送
	resp := &fasthttp.Response{}
	//請求-新增調(diào)用鏈
	/*
		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("系統(tǒng)錯誤")
	}
	//請求-響應(yīng)
	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)
}
//只有經(jīng)過token 驗證的才會加密 和解密
//handleFile 表示是否處理上傳文件, 默認網(wǎng)關(guān)不處理上傳文件的encryptString數(shù)據(jù), 如果處理會導(dǎo)致具體服務(wù)無法接收到具體參數(shù)
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)
			//這里輸出應(yīng)該密文 一旦加密解密調(diào)試好 這里就不會走進來
			response(c, 2001, "系統(tǒng)錯誤", err.Error())
			return
		}
	} else if isJsonRequest {
		err := parseJson(c, md5key)
		if err != nil {
			log("handleAes parseJson err:%v", err)
			//這里輸出應(yīng)該密文 一旦加密解密調(diào)試好 這里就不會走進來
			response(c, 2001, "系統(tǒng)錯誤", err.Error())
			return
		}
	} else if isFormUrl {
		err := parseForm(c, md5key)
		if err != nil {
			log("handleAes parseForm err:%v", err)
			//這里輸出應(yīng)該密文 一旦加密解密調(diào)試好 這里就不會走進來
			response(c, 2001, "系統(tǒng)錯誤", err.Error())
			return
		}
	} else if isFileRequest {
		err := parseFile(c, md5key)
		if err != nil {
			log("handleAes parseFile err:%v", err)
			//這里輸出應(yīng)該密文 一旦加密解密調(diào)試好 這里就不會走進來
			response(c, 2001, "系統(tǒng)錯誤", err.Error())
			return
		}
	}
	///截取 response body
	oldWriter := c.Writer
	blw := &aesWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
	c.Writer = blw
	// 走流程
	c.Next()
	///獲取返回數(shù)據(jù)
	responseByte := blw.body.Bytes()
	//日志
	c.Writer = oldWriter
	//如果返回的不是json格式 那么直接返回,應(yīng)為文件下載之類的不應(yīng)該加密
	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, "系統(tǒng)錯誤", err.Error())
		return
	}
	_, _ = c.Writer.WriteString(encryptStr)
}
//處理json
func parseJson(c *gin.Context, md5key string) error {
	//讀取數(shù)據(jù) body處理
	payload, err := c.GetRawData()
	if err != nil {
		return err
	}
	///解密body數(shù)據(jù) 請求的json是{"encryptString":{value}} value含有g(shù)cm的12字節(jié)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 {
	//讀取數(shù)據(jù) body處理
	payload, err := c.GetRawData()
	if err != nil {
		return err
	}
	///解密body數(shù)據(jù) 請求的json是"encryptString= value含有g(shù)cm的12字節(jié)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")
	}
	//準(zhǔn)備重寫數(shù)據(jù)
	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)
		}
	}
	//寫結(jié)尾標(biāo)志
	_ = 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 應(yīng)該是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 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.創(chuàng)建路由
	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
}

驗證

來讓我們一次驗證一下運行結(jié)果:

1.GET請求

2.看看post json

3驗證postform

最后來看一下文件上傳:

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

以上就是go GCM gin中間件的加密解密文件流處理的詳細內(nèi)容,更多關(guān)于go GCM gin加密解密文件流的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 探索Golang?Redis實現(xiàn)發(fā)布訂閱功能實例

    探索Golang?Redis實現(xiàn)發(fā)布訂閱功能實例

    這篇文章主要介紹了Golang?Redis發(fā)布訂閱功能實例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • 詳解go中panic源碼解讀

    詳解go中panic源碼解讀

    這篇文章主要介紹了go中panic源碼解讀,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • Go語言斷言和類型查詢的實現(xiàn)

    Go語言斷言和類型查詢的實現(xiàn)

    Go語言變量類型包含基礎(chǔ)類型和復(fù)合類型,類型斷言一般是對基礎(chǔ)類型的處理,本文主要介紹了Go語言斷言和類型查詢的實現(xiàn),感興趣的可以了解一下
    2024-01-01
  • golang-gorm自動建表問題

    golang-gorm自動建表問題

    這篇文章主要介紹了golang-gorm自動建表問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 舉例詳解Go語言中os庫的常用函數(shù)用法

    舉例詳解Go語言中os庫的常用函數(shù)用法

    這篇文章主要介紹了Go語言中os庫的常用函數(shù)用法,os函數(shù)的使用是Go語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • Go語言測試庫testify使用學(xué)習(xí)

    Go語言測試庫testify使用學(xué)習(xí)

    這篇文章主要為大家介紹了Go語言測試庫testify的使用學(xué)習(xí)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 詳解Go語言中Validator庫的使用方法和用途

    詳解Go語言中Validator庫的使用方法和用途

    github.com/go-playground/validator 是一個 Go 語言的庫,用于對結(jié)構(gòu)體字段進行驗證,它提供了一種簡單而靈活的方式來定義驗證規(guī)則,在這篇文章中,我們將從一個簡單的問題出發(fā),帶你了解 Validator 庫的用途,也會介紹Validator 的基本使用
    2023-09-09
  • Golang實現(xiàn)的聊天程序服務(wù)端和客戶端代碼分享

    Golang實現(xiàn)的聊天程序服務(wù)端和客戶端代碼分享

    這篇文章主要介紹了Golang實現(xiàn)的聊天程序服務(wù)端和客戶端代碼分享,本文先是講解了實現(xiàn)邏輯,然后給出了實現(xiàn)代碼,需要的朋友可以參考下
    2014-10-10
  • Windows下在CMD下執(zhí)行Go出現(xiàn)中文亂碼的解決方法

    Windows下在CMD下執(zhí)行Go出現(xiàn)中文亂碼的解決方法

    在cmd下運行g(shù)o程序或者是GOLAND的Terminal下運行g(shù)o程序會出現(xiàn)中文亂碼的情況。本文就詳細的介紹下解決方法,具有一定的參考價值,感興趣的可以了解一下
    2021-12-12
  • 詳解如何在Go服務(wù)中做鏈路追蹤

    詳解如何在Go服務(wù)中做鏈路追蹤

    使用 Go 語言開發(fā)微服務(wù)的時候,需要追蹤每一個請求的訪問鏈路,本文主要介紹了如何在Go 服務(wù)中做鏈路追蹤,感興趣的可以了解一下
    2021-09-09

最新評論