f
This commit is contained in:
@@ -2,11 +2,11 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/captcha"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -35,24 +35,58 @@ 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
|
||||
}
|
||||
|
||||
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq, clientIP string) error {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 加密手机号失败: %v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
|
||||
// 1. 滑块验证码校验(可选)
|
||||
cfg := l.svcCtx.Config.Captcha
|
||||
captchaResult := captcha.VerifyOptional(captcha.Config{
|
||||
AccessKeyID: cfg.AccessKeyID,
|
||||
AccessKeySecret: cfg.AccessKeySecret,
|
||||
EndpointURL: cfg.EndpointURL,
|
||||
SceneID: cfg.SceneID,
|
||||
}, req.CaptchaVerifyParam)
|
||||
|
||||
if captchaResult.VerifyErr != nil {
|
||||
return captchaResult.VerifyErr
|
||||
}
|
||||
|
||||
// 2. 防刷策略
|
||||
if captchaResult.Skipped {
|
||||
// 没有滑块验证码,使用更严格的限流策略
|
||||
// 2.1 IP 限流:同一 IP 每小时最多发送 10 次
|
||||
ipLimitKey := fmt.Sprintf("ip_limit:%s", clientIP)
|
||||
ipCount, err := l.svcCtx.Redis.Incr(ipLimitKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取IP限流缓存失败: %v", err)
|
||||
}
|
||||
if ipCount == 1 {
|
||||
// 第一次访问,设置 1 小时过期
|
||||
l.svcCtx.Redis.Expire(ipLimitKey, 3600)
|
||||
}
|
||||
if ipCount > 10 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("请求过于频繁,请稍后再试"), "短信发送, IP限流: %s, count: %d", clientIP, ipCount)
|
||||
}
|
||||
|
||||
// 2.2 手机号限流:同一手机号每小时最多发送 5 次(无滑块时更严格)
|
||||
hourLimitKey := fmt.Sprintf("hour_limit:%s:%s", req.ActionType, encryptedMobile)
|
||||
hourCount, err := l.svcCtx.Redis.Incr(hourLimitKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取小时限流缓存失败: %v", err)
|
||||
}
|
||||
if hourCount == 1 {
|
||||
l.svcCtx.Redis.Expire(hourLimitKey, 3600)
|
||||
}
|
||||
if hourCount > 5 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该手机号请求过于频繁,请稍后再试"), "短信发送, 手机号小时限流: %s, count: %d", encryptedMobile, hourCount)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查手机号是否在一分钟内已发送过验证码(通用)
|
||||
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile)
|
||||
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
|
||||
if err != nil {
|
||||
@@ -60,7 +94,6 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||
}
|
||||
|
||||
if exists {
|
||||
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
|
||||
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送验证码: %s", encryptedMobile)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user