From 60aac5ac94b476af3611696baa1f7a4f45b61a49 Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Sat, 28 Feb 2026 15:43:09 +0800 Subject: [PATCH] f --- .../api/internal/logic/auth/sendsmslogic.go | 17 +++- pkg/captcha/aliyun.go | 77 +++++++++++++++++++ pkg/captcha/encrypt_scene.go | 46 +++++++++++ 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 pkg/captcha/aliyun.go create mode 100644 pkg/captcha/encrypt_scene.go diff --git a/app/main/api/internal/logic/auth/sendsmslogic.go b/app/main/api/internal/logic/auth/sendsmslogic.go index c61e68c..a93fe9f 100644 --- a/app/main/api/internal/logic/auth/sendsmslogic.go +++ b/app/main/api/internal/logic/auth/sendsmslogic.go @@ -2,11 +2,12 @@ package auth import ( "context" - "xingfucha-server/common/xerr" - "xingfucha-server/pkg/lzkit/crypto" "fmt" "math/rand" "time" + "xingfucha-server/common/xerr" + "xingfucha-server/pkg/captcha" + "xingfucha-server/pkg/lzkit/crypto" "github.com/pkg/errors" @@ -35,6 +36,18 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo } func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error { + // 1. 图形验证码校验 + cfg := l.svcCtx.Config.Captcha + if err := captcha.Verify(captcha.Config{ + AccessKeyID: cfg.AccessKeyID, + AccessKeySecret: cfg.AccessKeySecret, + EndpointURL: cfg.EndpointURL, + SceneID: cfg.SceneID, + }, req.CaptchaVerifyParam); err != nil { + return err + } + + // 2. 原有的手机号加密逻辑 secretKey := l.svcCtx.Config.Encrypt.SecretKey encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey) if err != nil { diff --git a/pkg/captcha/aliyun.go b/pkg/captcha/aliyun.go new file mode 100644 index 0000000..806509a --- /dev/null +++ b/pkg/captcha/aliyun.go @@ -0,0 +1,77 @@ +package captcha + +import ( + "fmt" + "os" + + "xingfucha-server/common/xerr" + + captcha20230305 "github.com/alibabacloud-go/captcha-20230305/client" + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" + "github.com/pkg/errors" +) + +// Config 验证码配置 +type Config struct { + AccessKeyID string + AccessKeySecret string + EndpointURL string + SceneID string + EKey string // 加密模式用的 ekey +} + +// Verify 验证图形验证码 +func Verify(cfg Config, captchaVerifyParam string) error { + // 开发环境跳过验证 + if os.Getenv("ENV") == "development" { + return nil + } + + if captchaVerifyParam == "" { + return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam") + } + + // 创建阿里云验证码客户端 + clientCfg := &openapi.Config{ + AccessKeyId: tea.String(cfg.AccessKeyID), + AccessKeySecret: tea.String(cfg.AccessKeySecret), + Endpoint: tea.String(cfg.EndpointURL), + } + client, err := captcha20230305.NewClient(clientCfg) + if err != nil { + // 阿里云服务异常时,记录日志但视为通过,不影响业务可用性 + fmt.Printf("captcha.NewClient error: %+v\n", err) + return nil + } + + // 构建验证请求 + req := &captcha20230305.VerifyIntelligentCaptchaRequest{ + SceneId: tea.String(cfg.SceneID), + CaptchaVerifyParam: tea.String(captchaVerifyParam), + } + + // 调用验证接口 + runtime := &service.RuntimeOptions{} + resp, err := client.VerifyIntelligentCaptchaWithOptions(req, runtime) + if err != nil { + // 阿里云服务异常时,记录日志但视为通过,不影响业务可用性 + fmt.Printf("captcha.VerifyIntelligentCaptchaWithOptions error: %+v\n", err) + return nil + } + + if resp == nil || resp.Body == nil || resp.Body.Result == nil { + // 阿里云服务异常时,记录日志但视为通过,不影响业务可用性 + fmt.Printf("captcha response is nil or Body is nil or Result is nil\n") + return nil + } + + // 检查验证结果 + if tea.BoolValue(resp.Body.Result.VerifyResult) { + return nil + } + + // 验证失败 + return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "captcha verify failed") +} diff --git a/pkg/captcha/encrypt_scene.go b/pkg/captcha/encrypt_scene.go new file mode 100644 index 0000000..11aeec7 --- /dev/null +++ b/pkg/captcha/encrypt_scene.go @@ -0,0 +1,46 @@ +package captcha + +import ( + "encoding/base64" + "fmt" + "time" + + lzcrypto "xingfucha-server/pkg/lzkit/crypto" +) + +// GenerateEncryptedSceneID 生成加密场景ID +// sceneId: 场景ID +// ekey: 加密密钥(Base64编码) +// expireSeconds: 过期时间(秒) +// 返回: 加密后的场景ID(Base64编码) +func GenerateEncryptedSceneID(sceneId, ekey string, expireSeconds int) (string, error) { + // 默认过期时间1小时 + if expireSeconds <= 0 || expireSeconds > 86400 { + expireSeconds = 3600 + } + + // 生成时间戳 + ts := time.Now().Unix() + + // 构建明文: sceneId×tamp&expireTime + plaintext := fmt.Sprintf("%s&%d&%d", sceneId, ts, expireSeconds) + + // Base64解码ekey + keyBytes, err := base64.StdEncoding.DecodeString(ekey) + if err != nil { + return "", fmt.Errorf("decode ekey error: %w", err) + } + + // 检查密钥长度(必须是32字节) + if len(keyBytes) != 32 { + return "", fmt.Errorf("invalid ekey length, need 32 bytes after base64 decode, got %d", len(keyBytes)) + } + + // 使用AES加密 + encrypted, err := lzcrypto.AesEncrypt([]byte(plaintext), keyBytes) + if err != nil { + return "", fmt.Errorf("aes encrypt error: %w", err) + } + + return encrypted, nil +}