go GCM gin中间件的加密解密文件流处理
来源:脚本之家
2022-12-31 10:00:42
0浏览
收藏
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)
<p>再来看看网关转发程序proxy.go</p>
<pre class="brush: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)
<p>最后我们来写一个demo程序main.go</p>
<pre class="brush: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删除
go smtp实现邮件发送示例详解
- 上一篇
- go smtp实现邮件发送示例详解
- 下一篇
- 使用Go实现健壮的内存型缓存的方法
查看更多
最新文章
-
- Golang · Go教程 | 18分钟前 |
- Golang如何判断变量类型?
- 393浏览 收藏
-
- Golang · Go教程 | 27分钟前 |
- Golang云原生微服务实战教程
- 310浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang迭代器与懒加载结合应用
- 110浏览 收藏
-
- Golang · Go教程 | 1小时前 | 性能优化 并发安全 Golangslicemap 预设容量 指针拷贝
- Golangslicemap优化技巧分享
- 412浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang代理模式与访问控制实现解析
- 423浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang事件管理模块实现教程
- 274浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang接口多态实现全解析
- 241浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- GolangHTTP优化与中间件组合技巧
- 365浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang模块版本管理与升级技巧
- 247浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
查看更多
AI推荐
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3162次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3375次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3403次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4506次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3784次使用
查看更多
相关文章
-
- Go开发Gin项目添加jwt功能实例详解
- 2023-01-01 375浏览
-
- golang gin框架实现大文件的流式上传功能
- 2022-12-25 459浏览
-
- Golang详细讲解常用Http库及Gin框架的应用
- 2023-01-18 139浏览
-
- golang时区问题,怎么比较两个时间戳的差?
- 2023-01-21 288浏览
-
- Gin为JSON/XML/HTML渲染提供了易于使用的API
- 2023-01-17 247浏览

