golang-yitisheng-server/server/middleware/security.go

111 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/",
}
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...)
}