// Package utility 安全请求,思路来源https://blog.csdn.net/David_jiahuan/article/details/106391956 // @Author 铁憨憨[cory] 2024/9/11 9:38:00 package utility import ( "crypto/md5" "crypto/sha256" "encoding/hex" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/util/gconv" "math/big" "strconv" "time" ) // 签名验证中间件 func SignatureMiddleware(r *ghttp.Request) { if r.URL.Path == "/zm/api/wxApplet/wxApplet/appPort/releaseAppVersion" { r.Middleware.Next() return } // 获取请求中的参数 signature := gconv.String(r.Header.Get("sign")) timestamp := gconv.Int64(r.Header.Get("timestamp")) nonce := gconv.String(r.Header.Get("nonce")) if signature == "" || timestamp == 0 || nonce == "" { r.Response.WriteJson(g.Map{"error": "无效验证"}) r.ExitAll() return } //验证时间戳 if !validateTimestamp(timestamp) { r.Response.WriteJson(g.Map{"error": "请求过期"}) r.ExitAll() return } // 验证 nonce 防止重放攻击 if !validateNonce(nonce) { r.Response.WriteJson(g.Map{"error": "无效请求:检测到重放"}) r.ExitAll() return } serverSignature := generateServerSignature(strconv.FormatInt(timestamp, 10), nonce) // 验证签名 if signature != serverSignature { r.Response.WriteJson(g.Map{"error": "签名错误"}) r.ExitAll() return } //签名验证通过 r.Middleware.Next() } func validateTimestamp(unixTimestamp int64) bool { // 获取当前时间 now := time.Now().UnixMilli() result := subtractBigFromInt64(now, unixTimestamp) // 将整数 1500 转换为 *big.Int 类型 big1500 := big.NewInt(30000) // 验证时间差是否在 1500 毫秒内 if result.Cmp(big1500) == -1 && result.Cmp(big.NewInt(0)) >= 0 { return true } return false } func subtractBigFromInt64(currentMillis int64, unixTimestamp int64) *big.Int { // 将两个 int64 类型的值转换为 *big.Int 类型 bigCurrent := big.NewInt(currentMillis) bigUnixTimestamp := big.NewInt(unixTimestamp) // 计算差值 result := big.NewInt(0).Sub(bigCurrent, bigUnixTimestamp) // 求差值的绝对值 return result.Abs(result) } // 验证 nonce func validateNonce(nonce string) bool { nonceKey := "reset" + nonce // 使用 Redis 来存储 nonce,防止重放 redisClient := g.Redis() ctx := gctx.New() exists, err := redisClient.Exists(ctx, nonceKey) if err != nil { return false } if exists > 0 { return false // nonce 已经存在,说明是重放请求 } // nonce 不存在,存储到 Redis 并设置60秒过期时间 err = redisClient.SetEX(ctx, nonceKey, nonce, 60) if err != nil { return false } else { return true } } // 生成服务端签名 func generateServerSignature(timestamp, nonce string) string { sum := md5.Sum([]byte("coyrOrtiehanhan1223202409111457")) md5Sum := hex.EncodeToString(sum[:]) // 拼接时间戳、nonce 和 secretKey signStr := md5Sum + timestamp + nonce // 对拼接后的字符串进行 MD5 加密 md5Hash := md5.Sum([]byte(signStr)) md5Hex := hex.EncodeToString(md5Hash[:]) // 对 MD5 结果进行 SHA-256 加密 sha256Hash := sha256.Sum256([]byte(md5Hex)) sha256Hex := hex.EncodeToString(sha256Hash[:]) return sha256Hex }