115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
|
// 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
|
|||
|
}
|