f
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -11,6 +13,8 @@ import (
|
||||
"tyapi-server/internal/application/user/dto/commands"
|
||||
"tyapi-server/internal/application/user/dto/queries"
|
||||
_ "tyapi-server/internal/application/user/dto/responses"
|
||||
"tyapi-server/internal/config"
|
||||
"tyapi-server/internal/shared/crypto"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
"tyapi-server/internal/shared/middleware"
|
||||
)
|
||||
@@ -22,6 +26,7 @@ type UserHandler struct {
|
||||
validator interfaces.RequestValidator
|
||||
logger *zap.Logger
|
||||
jwtAuth *middleware.JWTAuthMiddleware
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// NewUserHandler 创建用户处理器
|
||||
@@ -31,6 +36,7 @@ func NewUserHandler(
|
||||
validator interfaces.RequestValidator,
|
||||
logger *zap.Logger,
|
||||
jwtAuth *middleware.JWTAuthMiddleware,
|
||||
cfg *config.Config,
|
||||
) *UserHandler {
|
||||
return &UserHandler{
|
||||
appService: appService,
|
||||
@@ -38,16 +44,26 @@ func NewUserHandler(
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
jwtAuth: jwtAuth,
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// decodedSendCodeData 解码后的请求数据结构
|
||||
type decodedSendCodeData struct {
|
||||
Phone string `json:"phone"`
|
||||
Scene string `json:"scene"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Nonce string `json:"nonce"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// SendCode 发送验证码
|
||||
// @Summary 发送短信验证码
|
||||
// @Description 向指定手机号发送验证码,支持注册、登录、修改密码等场景
|
||||
// @Description 向指定手机号发送验证码,支持注册、登录、修改密码等场景。需要提供有效的签名验证。只接收编码后的data字段(使用自定义编码方案)
|
||||
// @Tags 用户认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body commands.SendCodeCommand true "发送验证码请求"
|
||||
// @Param request body commands.SendCodeCommand true "发送验证码请求(只包含data字段)"
|
||||
// @Success 200 {object} map[string]interface{} "验证码发送成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 429 {object} map[string]interface{} "请求频率限制"
|
||||
@@ -55,14 +71,61 @@ func NewUserHandler(
|
||||
// @Router /api/v1/users/send-code [post]
|
||||
func (h *UserHandler) SendCode(c *gin.Context) {
|
||||
var cmd commands.SendCodeCommand
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
|
||||
// 绑定请求(只包含data字段)
|
||||
if err := c.ShouldBindJSON(&cmd); err != nil {
|
||||
h.response.BadRequest(c, "请求参数格式错误,必须提供data字段")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证data字段不为空
|
||||
if cmd.Data == "" {
|
||||
h.response.BadRequest(c, "data字段不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 解码自定义编码的数据
|
||||
decodedData, err := h.decodeRequestData(cmd.Data)
|
||||
if err != nil {
|
||||
h.logger.Warn("解码请求数据失败",
|
||||
zap.String("client_ip", c.ClientIP()),
|
||||
zap.Error(err))
|
||||
h.response.BadRequest(c, "请求数据解码失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必要字段
|
||||
if decodedData.Phone == "" || decodedData.Scene == "" {
|
||||
h.response.BadRequest(c, "手机号和场景不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果启用了签名验证,进行签名校验
|
||||
if h.config.SMS.SignatureEnabled {
|
||||
if err := h.verifyDecodedSignature(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, "签名验证失败,请求无效")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 构建SendCodeCommand用于调用应用服务
|
||||
serviceCmd := &commands.SendCodeCommand{
|
||||
Phone: decodedData.Phone,
|
||||
Scene: decodedData.Scene,
|
||||
Timestamp: decodedData.Timestamp,
|
||||
Nonce: decodedData.Nonce,
|
||||
Signature: decodedData.Signature,
|
||||
}
|
||||
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
|
||||
if err := h.appService.SendCode(c.Request.Context(), &cmd, clientIP, userAgent); err != nil {
|
||||
if err := h.appService.SendCode(c.Request.Context(), serviceCmd, clientIP, userAgent); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -70,6 +133,42 @@ func (h *UserHandler) SendCode(c *gin.Context) {
|
||||
h.response.Success(c, nil, "验证码发送成功")
|
||||
}
|
||||
|
||||
// decodeRequestData 解码自定义编码的请求数据
|
||||
func (h *UserHandler) decodeRequestData(encodedData string) (*decodedSendCodeData, error) {
|
||||
// 使用自定义编码方案解码
|
||||
decodedData, err := crypto.DecodeRequest(encodedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("自定义编码解码失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析JSON
|
||||
var decoded decodedSendCodeData
|
||||
if err := json.Unmarshal([]byte(decodedData), &decoded); err != nil {
|
||||
return nil, fmt.Errorf("JSON解析失败: %w", err)
|
||||
}
|
||||
|
||||
return &decoded, nil
|
||||
}
|
||||
|
||||
// verifyDecodedSignature 验证解码后的签名
|
||||
func (h *UserHandler) verifyDecodedSignature(data *decodedSendCodeData) error {
|
||||
// 构建参数map(包含signature字段,VerifySignature会自动排除它)
|
||||
params := map[string]string{
|
||||
"phone": data.Phone,
|
||||
"scene": data.Scene,
|
||||
"signature": data.Signature,
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
return crypto.VerifySignature(
|
||||
params,
|
||||
h.config.SMS.SignatureSecret,
|
||||
data.Timestamp,
|
||||
data.Nonce,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Register 用户注册
|
||||
// @Summary 用户注册
|
||||
// @Description 使用手机号、密码和验证码进行用户注册,需要确认密码
|
||||
|
||||
Reference in New Issue
Block a user