package sign import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" "log" "net/url" "strconv" "time" "sms-receiver-go/config" ) // GenerateSign 生成签名 func GenerateSign(timestamp int64, secret string) (string, error) { if secret == "" { return "", fmt.Errorf("secret 不能为空") } stringToSign := strconv.FormatInt(timestamp, 10) + "\n" + secret hmacCode := hmac.New(sha256.New, []byte(secret)) hmacCode.Write([]byte(stringToSign)) signBytes := hmacCode.Sum(nil) // Base64 编码 signBase64 := base64.StdEncoding.EncodeToString(signBytes) // URL 编码 sign := url.QueryEscape(signBase64) return sign, nil } // SignVerificationResult 签名验证结果 type SignVerificationResult struct { Valid bool Reason string TokenName string Timestamp int64 ServerTime int64 } // VerifySign 验证签名 func VerifySign(token string, timestamp int64, sign string, cfg *config.SecurityConfig) (*SignVerificationResult, error) { serverTime := time.Now().UnixMilli() // 如果未启用签名验证或未提供 token,直接通过 if !cfg.SignVerify { return &SignVerificationResult{ Valid: true, Reason: "签名验证未启用", Timestamp: timestamp, ServerTime: serverTime, }, nil } if token == "" { return &SignVerificationResult{ Valid: false, Reason: "未提供 token", Timestamp: timestamp, ServerTime: serverTime, }, nil } // 查找对应的 token 配置 tokenConfig := config.Get().GetTokenByValue(token) if tokenConfig == nil { return &SignVerificationResult{ Valid: false, Reason: "无效的 token", Timestamp: timestamp, ServerTime: serverTime, }, nil } secret := tokenConfig.Secret // 如果 secret 为空,则该 token 不要求签名验证 if secret == "" { return &SignVerificationResult{ Valid: true, Reason: "token 未配置 secret,跳过签名验证", TokenName: tokenConfig.Name, Timestamp: timestamp, ServerTime: serverTime, }, nil } // 检查时间戳是否过期 maxAge := int64(cfg.SignMaxAge) if maxAge == 0 { maxAge = 5 * 60 * 1000 // 默认5分钟 } timeDiff := serverTime - timestamp if timeDiff > maxAge { log.Printf("签名验证失败: 时间戳过期 - token=%s, timestamp=%d, time_diff=%dms, max_age=%dms", token, timestamp, timeDiff, maxAge) return &SignVerificationResult{ Valid: false, Reason: fmt.Sprintf("时间戳过期(差异: %.1f 秒)", float64(timeDiff)/1000), TokenName: tokenConfig.Name, Timestamp: timestamp, ServerTime: serverTime, }, nil } // 重新生成签名进行比较 expectedSign, err := GenerateSign(timestamp, secret) if err != nil { return nil, fmt.Errorf("生成签名失败: %w", err) } // 比较签名 if sign != expectedSign { log.Printf("签名验证失败: 签名不匹配 - token=%s, timestamp=%d, ip=unknown", token, timestamp) return &SignVerificationResult{ Valid: false, Reason: "签名不匹配", TokenName: tokenConfig.Name, Timestamp: timestamp, ServerTime: serverTime, }, nil } // 签名验证通过 log.Printf("签名验证成功: token=%s, timestamp=%d", token, timestamp) return &SignVerificationResult{ Valid: true, Reason: "签名验证通过", TokenName: tokenConfig.Name, Timestamp: timestamp, ServerTime: serverTime, }, nil }