中间件 Middleware
普通模式(Token验证)
默认为普通模式,使用 authMiddleware
中间件,验证Token。
服务运行于普通模式,调用管理接口时,请求头需要携带Header参数:
参数名 | 类型 | 描述 |
---|---|---|
Token | string | 认证Token,即 config.json 文件中的Token |
安全模式(签名验证)
可通过命令行启动参数 -s=true
,将服务运行于安全模式,使用 signatureMiddleware
中间件,验证签名。
服务运行于安全模式,调用管理接口时,请求头需要携带以下Header参数:
参数名 | 类型 | 描述 |
---|---|---|
Timestamp | int | 请求毫秒时间戳 |
Signature | string | 请求签名值 |
签名算法
data
:请求Body
数据timestamp
:毫秒时间戳token
:config.json
文件中的Token- 将以上值拼接
data + timestamp + token
得到待签名字符串
- 计算
待签名字符串
的MD5散列值
转换为16进制得到32个小写字符串
,即为签名值
Go示例代码
go
// generateSignature 生成签名
// 算法: data + timestamp + token 的MD5值
func generateSignature(data, timestamp, token string) string {
signData := data + timestamp + token
hash := md5.Sum([]byte(signData))
return hex.EncodeToString(hash[:])
}
// verifySignature 验证签名
// 算法: data + timestamp + token 的MD5值
func verifySignature(data, timestamp, token, signature string) bool {
expectedSignature := generateSignature(data, timestamp, token)
return expectedSignature == signature
}
中间件go
源码
go
// authMiddleware HTTP请求TOKEN验证中间件(带限流功能)
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
clientIP := c.ClientIP()
if authLimiter.IsBlocked(clientIP) {
c.JSON(http.StatusTooManyRequests, NewCustomErrorResponse(ErrCodeInvalidTokenFailedTooMany, "认证失败次数过多,IP已被封禁"))
c.Abort()
return
}
token := c.GetHeader("Token")
if token == "" {
//authLimiter.AddFailedAttempt(clientIP)
c.JSON(http.StatusUnauthorized, NewErrorResponse(ErrCodeMissingToken))
c.Abort()
return
}
configMutex.RLock()
expectedToken := config.Token
configMutex.RUnlock()
if token != expectedToken {
authLimiter.AddFailedAttempt(clientIP)
c.JSON(http.StatusUnauthorized, NewErrorResponse(ErrCodeInvalidToken))
c.Abort()
return
}
authLimiter.ResetFailedAttempts(clientIP)
c.Next()
}
}
go
// signatureMiddleware 签名验证中间件
func signatureMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
timestamp := c.GetHeader("Timestamp")
signature := c.GetHeader("Signature")
if timestamp == "" {
c.JSON(http.StatusBadRequest, NewErrorResponse(ErrCodeMissingTimestamp))
c.Abort()
return
}
if signature == "" {
c.JSON(http.StatusBadRequest, NewErrorResponse(ErrCodeMissingSignature))
c.Abort()
return
}
// 验证时间戳格式
ts, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, NewErrorResponse(ErrCodeInvalidTimestamp))
c.Abort()
return
}
// 检查时间戳是否过期(允许1分钟的时间差)
currentTime := time.Now().UnixMilli()
if absValue(currentTime-ts) > 60*1000 {
c.JSON(http.StatusBadRequest, NewErrorResponse(ErrCodeTimestampExpired))
c.Abort()
return
}
body, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, NewErrorResponse(ErrCodeReadBodyFailed))
return
}
configMutex.RLock()
token := config.Token
configMutex.RUnlock()
if !verifySignature(string(body), timestamp, token, signature) {
c.JSON(http.StatusUnauthorized, NewErrorResponse(ErrCodeInvalidSignature))
c.Abort()
return
}
c.Next()
}
}