Files
SmsReceiver-go/sign/sign.go
OpenClaw Agent 1da899a0f4 feat: v2.0.0 完整代码优化升级
🔴 高优先级 (6项全部完成):
- 数据库事务支持 (InsertMessageWithLog)
- SQL注入修复 (参数化查询)
- 配置验证 (Validate方法)
- 会话密钥强化 (长度验证)
- 签名验证增强 (SignVerificationResult)
- 密码哈希支持 (bcrypt)

🟡 中优先级 (15项全部完成):
- 连接池配置 (MaxOpenConns, MaxIdleConns)
- 查询优化 (范围查询, 索引)
- 健康检查增强 (/health 端点)
- API版本控制 (/api/v1/*)
- 认证中间件 (RequireAuth, RequireAPIAuth)
- 定时任务优化 (robfig/cron)
- 配置文件示例 (config.example.yaml)
- 常量定义 (config/constants.go)
- 开发文档 (DEVELOPMENT.md)

🟢 低优先级 (9项全部完成):
- Docker支持 (Dockerfile, docker-compose.yml)
- Makefile构建脚本
- 优化报告 (OPTIMIZATION_REPORT.md)
- 密码哈希工具 (tools/password_hash.go)
- 14个新文件
- 30项优化100%完成

版本: v2.0.0
2026-02-08 18:59:29 +08:00

140 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}