🔴 高优先级 (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
140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
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
|
||
}
|