This commit is contained in:
2026-02-12 13:36:24 +08:00
parent f400052f95
commit 47cbc5b3a5
2 changed files with 94 additions and 7 deletions

View File

@@ -2,9 +2,11 @@
package handlers
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@@ -27,6 +29,7 @@ type UserHandler struct {
logger *zap.Logger
jwtAuth *middleware.JWTAuthMiddleware
config *config.Config
cache interfaces.CacheService
}
// NewUserHandler 创建用户处理器
@@ -37,6 +40,7 @@ func NewUserHandler(
logger *zap.Logger,
jwtAuth *middleware.JWTAuthMiddleware,
cfg *config.Config,
cache interfaces.CacheService,
) *UserHandler {
return &UserHandler{
appService: appService,
@@ -45,6 +49,7 @@ func NewUserHandler(
logger: logger,
jwtAuth: jwtAuth,
config: cfg,
cache: cache,
}
}
@@ -100,15 +105,18 @@ func (h *UserHandler) SendCode(c *gin.Context) {
return
}
// 如果启用了签名验证,进行签名校验
// 如果启用了签名验证,进行签名校验包含nonce唯一性检查防止重放攻击
if h.config.SMS.SignatureEnabled {
if err := h.verifyDecodedSignature(decodedData); err != nil {
if err := h.verifyDecodedSignature(c.Request.Context(), decodedData); err != nil {
h.logger.Warn("短信发送签名验证失败",
zap.String("phone", decodedData.Phone),
zap.String("scene", decodedData.Scene),
zap.String("client_ip", c.ClientIP()),
zap.Error(err))
h.response.BadRequest(c, "签名验证失败,请求无效")
// 根据错误类型返回不同的用户友好消息(不暴露技术细节)
userMessage := h.getSignatureErrorMessage(err)
h.response.BadRequest(c, userMessage)
return
}
}
@@ -150,8 +158,8 @@ func (h *UserHandler) decodeRequestData(encodedData string) (*decodedSendCodeDat
return &decoded, nil
}
// verifyDecodedSignature 验证解码后的签名
func (h *UserHandler) verifyDecodedSignature(data *decodedSendCodeData) error {
// verifyDecodedSignature 验证解码后的签名包含nonce唯一性检查防止重放攻击
func (h *UserHandler) verifyDecodedSignature(ctx context.Context, data *decodedSendCodeData) error {
// 构建参数map包含signature字段VerifySignature会自动排除它
params := map[string]string{
"phone": data.Phone,
@@ -159,15 +167,40 @@ func (h *UserHandler) verifyDecodedSignature(data *decodedSendCodeData) error {
"signature": data.Signature,
}
// 验证签名
return crypto.VerifySignature(
// 验证签名并检查nonce唯一性防止重放攻击
return crypto.VerifySignatureWithNonceCheck(
ctx,
params,
h.config.SMS.SignatureSecret,
data.Timestamp,
data.Nonce,
h.cache,
"sms:signature", // 缓存键前缀
)
}
// getSignatureErrorMessage 根据错误类型返回用户友好的错误消息(不暴露技术细节)
func (h *UserHandler) getSignatureErrorMessage(err error) string {
errMsg := err.Error()
// 根据错误消息内容判断错误类型,返回通用的用户友好消息
if strings.Contains(errMsg, "请求已被使用") || strings.Contains(errMsg, "重复提交") {
// 重放攻击:返回通用消息,不暴露具体原因
return "请求无效,请重新操作"
}
if strings.Contains(errMsg, "时间戳") || strings.Contains(errMsg, "过期") {
// 时间戳过期:返回通用消息
return "请求已过期,请重新操作"
}
if strings.Contains(errMsg, "签名") {
// 签名错误:返回通用消息
return "请求验证失败,请重新操作"
}
// 其他错误:返回通用消息
return "请求验证失败,请重新操作"
}
// Register 用户注册
// @Summary 用户注册