112 lines
2.4 KiB
Go
112 lines
2.4 KiB
Go
// Package middleware 安全校验中间件
|
|
package middleware
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"strconv"
|
|
"time"
|
|
|
|
"server/common"
|
|
"server/config"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// SecurityMiddleware 安全校验中间件
|
|
// 防止暴力入侵,校验请求头签名
|
|
// 请求头需携带:
|
|
// - X-App-Sign: 签名值 = MD5(timestamp + secretKey)
|
|
// - X-App-Timestamp: 时间戳(毫秒)
|
|
func SecurityMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
cfg := config.AppConfig.Security
|
|
|
|
// 未启用则跳过
|
|
if !cfg.Enable {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// 白名单路径跳过
|
|
path := c.Request.URL.Path
|
|
if isSecurityWhitelist(path) {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
// 获取签名和时间戳
|
|
sign := c.GetHeader(cfg.HeaderKey)
|
|
timestamp := c.GetHeader("X-App-Timestamp")
|
|
|
|
if sign == "" || timestamp == "" {
|
|
common.Warn("安全校验失败: 缺少签名头 IP=%s Path=%s", c.ClientIP(), path)
|
|
common.Error(c, 403, "非法请求")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 验证时间戳 (5分钟内有效)
|
|
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
|
if err != nil {
|
|
common.Warn("安全校验失败: 时间戳格式错误 IP=%s", c.ClientIP())
|
|
common.Error(c, 403, "非法请求")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
now := time.Now().UnixMilli()
|
|
if abs(now-ts) > 5*60*1000 { // 5分钟
|
|
common.Warn("安全校验失败: 时间戳过期 IP=%s Timestamp=%d", c.ClientIP(), ts)
|
|
common.Error(c, 403, "请求已过期")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 验证签名
|
|
expectedSign := generateSign(timestamp, cfg.SecretKey)
|
|
if sign != expectedSign {
|
|
common.Warn("安全校验失败: 签名错误 IP=%s Sign=%s Expected=%s", c.ClientIP(), sign, expectedSign)
|
|
common.Error(c, 403, "签名错误")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// generateSign 生成签名
|
|
func generateSign(timestamp, secretKey string) string {
|
|
data := timestamp + secretKey
|
|
hash := md5.Sum([]byte(data))
|
|
return hex.EncodeToString(hash[:])
|
|
}
|
|
|
|
// 安全校验白名单
|
|
var securityWhitelist = []string{
|
|
"/swagger/",
|
|
"/swagger/index.html",
|
|
}
|
|
|
|
func isSecurityWhitelist(path string) bool {
|
|
for _, white := range securityWhitelist {
|
|
if len(path) >= len(white) && path[:len(white)] == white {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func abs(n int64) int64 {
|
|
if n < 0 {
|
|
return -n
|
|
}
|
|
return n
|
|
}
|
|
|
|
// AddSecurityWhitelist 添加安全校验白名单
|
|
func AddSecurityWhitelist(paths ...string) {
|
|
securityWhitelist = append(securityWhitelist, paths...)
|
|
}
|