Skip to content

中间件 Middleware

普通模式(Token验证)

默认为普通模式,使用 authMiddleware 中间件,验证Token。

服务运行于普通模式,调用管理接口时,请求头需要携带Header参数:

参数名类型描述
Tokenstring认证Token,即 config.json 文件中的Token

安全模式(签名验证)

可通过命令行启动参数 -s=true,将服务运行于安全模式,使用 signatureMiddleware 中间件,验证签名。

服务运行于安全模式,调用管理接口时,请求头需要携带以下Header参数:

参数名类型描述
Timestampint请求毫秒时间戳
Signaturestring请求签名值

签名算法

  1. data:请求Body数据
  2. timestamp:毫秒时间戳
  3. tokenconfig.json 文件中的Token
  4. 将以上值拼接 data + timestamp + token 得到 待签名字符串
  5. 计算 待签名字符串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()
	}
}