Files
qnc-server-v3/pkg/captcha/aliyun.go
2026-02-28 14:15:07 +08:00

154 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package captcha
import (
"os"
"strings"
"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
}
// VerifyResult 验证结果
type VerifyResult struct {
Verified bool // 是否通过滑块验证
Skipped bool // 是否跳过验证(无滑块参数)
VerifyErr error // 验证错误(如果有)
}
// 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
// 当 captchaVerifyParam 为空时返回 Skipped=true由调用方决定后续处理
func VerifyOptional(cfg Config, captchaVerifyParam string) VerifyResult {
// 开发环境可跳过验证
if os.Getenv("ENV") == "development" {
return VerifyResult{Verified: true, Skipped: false}
}
// 没有滑块验证码参数,返回跳过状态
if captchaVerifyParam == "" {
return VerifyResult{Skipped: true}
}
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)),
}
}
// Verify 验证阿里云验证码(必须提供验证码参数,兼容旧接口)
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
}