Files
qnc-server-v3/pkg/captcha/aliyun.go

154 lines
4.9 KiB
Go
Raw Normal View History

2026-02-26 10:48:38 +08:00
package captcha
import (
"os"
2026-02-28 14:15:07 +08:00
"strings"
2026-02-26 10:48:38 +08:00
"github.com/pkg/errors"
"qnc-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/tea"
)
type Config struct {
AccessKeyID string
AccessKeySecret string
EndpointURL string
SceneID string
}
2026-02-28 11:35:55 +08:00
// VerifyResult 验证结果
type VerifyResult struct {
Verified bool // 是否通过滑块验证
Skipped bool // 是否跳过验证(无滑块参数)
VerifyErr error // 验证错误(如果有)
}
2026-02-28 14:15:07 +08:00
// isWeChatUserAgent 检测是否为微信浏览器的 User-Agent
func isWeChatUserAgent(userAgent string) bool {
if userAgent == "" {
return false
}
ua := strings.ToLower(userAgent)
return strings.Contains(ua, "micromessenger") || strings.Contains(ua, "wechat")
}
// VerifyOptionalWithUserAgent 可选验证阿里云验证码(支持微信环境跳过验证)
// 当 captchaVerifyParam 为空时返回 Skipped=true由调用方决定后续处理
// 当 userAgent 为微信浏览器时,跳过验证返回 Verified=true
func VerifyOptionalWithUserAgent(cfg Config, captchaVerifyParam string, userAgent string) VerifyResult {
// 开发环境可跳过验证
if os.Getenv("ENV") == "development" {
return VerifyResult{Verified: true, Skipped: false}
}
// 微信环境下跳过图形验证码校验
if isWeChatUserAgent(userAgent) {
return VerifyResult{Verified: true, Skipped: false}
}
// 没有滑块验证码参数,报错提示
if captchaVerifyParam == "" {
return VerifyResult{VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")}
}
clientCfg := &openapi.Config{
AccessKeyId: tea.String(cfg.AccessKeyID),
AccessKeySecret: tea.String(cfg.AccessKeySecret),
}
clientCfg.Endpoint = tea.String(cfg.EndpointURL)
client, err := captcha20230305.NewClient(clientCfg)
if err != nil {
return VerifyResult{VerifyErr: errors.Wrapf(err, "create aliyun captcha client error")}
}
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
SceneId: tea.String(cfg.SceneID),
CaptchaVerifyParam: tea.String(captchaVerifyParam),
}
resp, err := client.VerifyIntelligentCaptcha(req)
if err != nil {
return VerifyResult{VerifyErr: errors.Wrapf(err, "verify aliyun captcha error")}
}
if tea.BoolValue(resp.Body.Result.VerifyResult) {
return VerifyResult{Verified: true}
}
return VerifyResult{
VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "aliyun captcha verify failed: code=%s, msg=%s",
tea.StringValue(resp.Body.Code), tea.StringValue(resp.Body.Message)),
}
}
// VerifyWithUserAgent 验证阿里云验证码(必须提供验证码参数,支持微信环境跳过验证)
func VerifyWithUserAgent(cfg Config, captchaVerifyParam string, userAgent string) error {
result := VerifyOptionalWithUserAgent(cfg, captchaVerifyParam, userAgent)
if result.VerifyErr != nil {
return result.VerifyErr
}
if result.Skipped {
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")
}
return nil
}
// VerifyOptional 可选验证阿里云验证码(兼容旧接口,不判断 User-Agent
2026-02-28 11:35:55 +08:00
// 当 captchaVerifyParam 为空时返回 Skipped=true由调用方决定后续处理
func VerifyOptional(cfg Config, captchaVerifyParam string) VerifyResult {
2026-02-26 10:48:38 +08:00
// 开发环境可跳过验证
if os.Getenv("ENV") == "development" {
2026-02-28 11:35:55 +08:00
return VerifyResult{Verified: true, Skipped: false}
2026-02-26 10:48:38 +08:00
}
2026-02-28 11:35:55 +08:00
// 没有滑块验证码参数,返回跳过状态
2026-02-26 10:48:38 +08:00
if captchaVerifyParam == "" {
2026-02-28 11:35:55 +08:00
return VerifyResult{Skipped: true}
2026-02-26 10:48:38 +08:00
}
clientCfg := &openapi.Config{
AccessKeyId: tea.String(cfg.AccessKeyID),
AccessKeySecret: tea.String(cfg.AccessKeySecret),
}
clientCfg.Endpoint = tea.String(cfg.EndpointURL)
client, err := captcha20230305.NewClient(clientCfg)
if err != nil {
2026-02-28 11:35:55 +08:00
return VerifyResult{VerifyErr: errors.Wrapf(err, "create aliyun captcha client error")}
2026-02-26 10:48:38 +08:00
}
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
SceneId: tea.String(cfg.SceneID),
CaptchaVerifyParam: tea.String(captchaVerifyParam),
}
resp, err := client.VerifyIntelligentCaptcha(req)
if err != nil {
2026-02-28 11:35:55 +08:00
return VerifyResult{VerifyErr: errors.Wrapf(err, "verify aliyun captcha error")}
2026-02-26 10:48:38 +08:00
}
if tea.BoolValue(resp.Body.Result.VerifyResult) {
2026-02-28 11:35:55 +08:00
return VerifyResult{Verified: true}
2026-02-26 10:48:38 +08:00
}
2026-02-28 11:35:55 +08:00
return VerifyResult{
VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "aliyun captcha verify failed: code=%s, msg=%s",
tea.StringValue(resp.Body.Code), tea.StringValue(resp.Body.Message)),
}
}
2026-02-28 14:15:07 +08:00
// Verify 验证阿里云验证码(必须提供验证码参数,兼容旧接口)
2026-02-28 11:35:55 +08:00
func Verify(cfg Config, captchaVerifyParam string) error {
result := VerifyOptional(cfg, captchaVerifyParam)
if result.VerifyErr != nil {
return result.VerifyErr
}
if result.Skipped {
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")
}
return nil
2026-02-26 10:48:38 +08:00
}