diff --git a/app/main/api/internal/handler/auth/sendsmshandler.go b/app/main/api/internal/handler/auth/sendsmshandler.go index 313fc7c..cca2b94 100644 --- a/app/main/api/internal/handler/auth/sendsmshandler.go +++ b/app/main/api/internal/handler/auth/sendsmshandler.go @@ -1,12 +1,14 @@ package auth import ( + "context" "net/http" "tydata-server/app/main/api/internal/logic/auth" "tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/types" "tydata-server/common/result" + "tydata-server/pkg/captcha" "tydata-server/pkg/lzkit/validator" "github.com/zeromicro/go-zero/rest/httpx" @@ -23,7 +25,9 @@ func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { result.ParamValidateErrorResult(r, w, err) return } - l := auth.NewSendSmsLogic(r.Context(), svcCtx) + // 将 request 注入 context,供 captcha 包判断微信等环境时跳过图形验证 + ctx := context.WithValue(r.Context(), captcha.HTTPRequestContextKey, r) + l := auth.NewSendSmsLogic(ctx, svcCtx) err := l.SendSms(&req) result.HttpResult(r, w, nil, err) } diff --git a/app/main/api/internal/logic/auth/sendsmslogic.go b/app/main/api/internal/logic/auth/sendsmslogic.go index 1d72c3b..645b8d3 100644 --- a/app/main/api/internal/logic/auth/sendsmslogic.go +++ b/app/main/api/internal/logic/auth/sendsmslogic.go @@ -37,12 +37,12 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error { cfg := l.svcCtx.Config.Captcha - if err := captcha.Verify(captcha.Config{ + if err := captcha.VerifyWithRequest(captcha.Config{ AccessKeyID: cfg.AccessKeyID, AccessKeySecret: cfg.AccessKeySecret, EndpointURL: cfg.EndpointURL, SceneID: cfg.SceneID, - }, req.CaptchaVerifyParam); err != nil { + }, req.CaptchaVerifyParam, l.ctx); err != nil { return err } // 默认action类型:当未传入时,默认为login,便于小程序环境兼容 diff --git a/pkg/captcha/aliyun.go b/pkg/captcha/aliyun.go index d338d9d..e1742b4 100644 --- a/pkg/captcha/aliyun.go +++ b/pkg/captcha/aliyun.go @@ -1,7 +1,10 @@ package captcha import ( + "context" + "net/http" "os" + "strings" "tydata-server/common/xerr" @@ -11,6 +14,21 @@ import ( "github.com/pkg/errors" ) +// contextKey 用于在 context 中存储 *http.Request,供 VerifyWithRequest 判断微信等环境 +type contextKey struct{} + +// HTTPRequestContextKey 为 context 中 http.Request 的 key,handler 可将 r 注入后传入 logic +var HTTPRequestContextKey = &contextKey{} + +// isWeChatClient 根据 User-Agent 判断是否为微信内置浏览器/小程序环境(此类环境可不传 captchaVerifyParam,默认通过) +func isWeChatClient(r *http.Request) bool { + if r == nil { + return false + } + ua := strings.ToLower(r.Header.Get("User-Agent")) + return strings.Contains(ua, "micromessenger") || strings.Contains(ua, "miniprogram") +} + type Config struct { AccessKeyID string AccessKeySecret string @@ -18,13 +36,24 @@ type Config struct { SceneID string } +// VerifyWithRequest 在 Verify 基础上支持按请求头判断:微信/小程序环境下可不传 captchaVerifyParam,默认通过。 +// 若 ctx 中带有 HTTPRequestContextKey 的 *http.Request,且 User-Agent 为微信环境,则不再校验 captchaVerifyParam。 +func VerifyWithRequest(cfg Config, captchaVerifyParam string, ctx context.Context) error { + if ctx != nil { + if req, ok := ctx.Value(HTTPRequestContextKey).(*http.Request); ok && isWeChatClient(req) { + return nil + } + } + return Verify(cfg, captchaVerifyParam) +} + func Verify(cfg Config, captchaVerifyParam string) error { if os.Getenv("ENV") == "development" { return nil } - // captchaVerifyParam 为可选入参,未传入时默认验证通过(兼容未做防滑拼图的页面) + if captchaVerifyParam == "" { - return nil + return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败,captchaVerifyParam 不能为空"), "empty captchaVerifyParam") } clientCfg := &openapi.Config{