Files
story/internal/onboarding/tencent_captcha.go
T

106 lines
3.4 KiB
Go
Raw Normal View History

package onboarding
import (
"context"
"data-terminal-cloud/internal/config"
captcha "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/captcha/v20190722"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"net"
"strconv"
"strings"
)
type CaptchaProvider interface {
AppID(client string) string
Verify(ctx context.Context, client, ticket, randstr, ip string) error
}
type captchaAPI interface {
DescribeCaptchaResultWithContext(context.Context, *captcha.DescribeCaptchaResultRequest) (*captcha.DescribeCaptchaResultResponse, error)
DescribeCaptchaMiniResultWithContext(context.Context, *captcha.DescribeCaptchaMiniResultRequest) (*captcha.DescribeCaptchaMiniResultResponse, error)
}
type tencentCaptchaProvider struct {
api captchaAPI
config config.TencentCaptchaConfig
}
func NewTencentCaptchaProvider(c config.TencentCaptchaConfig) (CaptchaProvider, error) {
if err := c.Validate(); err != nil {
return nil, err
}
if c.SecretID == "" {
return nil, nil
}
p := profile.NewClientProfile()
p.HttpProfile.Endpoint = "captcha.tencentcloudapi.com"
p.HttpProfile.ReqTimeout = 8
p.NetworkFailureMaxRetries = 0
p.RateLimitExceededMaxRetries = 0
p.UnsafeRetryOnConnectionFailure = false
api, err := captcha.NewClient(common.NewCredential(c.SecretID, c.SecretKey), "", p)
if err != nil {
return nil, err
}
return &tencentCaptchaProvider{api: api, config: c}, nil
}
func (p *tencentCaptchaProvider) clientConfig(client string) config.CaptchaClientConfig {
if client == "mini_program" {
return p.config.Mini
}
if client == "saas" {
return p.config.SaaS
}
return config.CaptchaClientConfig{}
}
func (p *tencentCaptchaProvider) AppID(client string) string { return p.clientConfig(client).AppID }
func (p *tencentCaptchaProvider) Verify(ctx context.Context, client, ticket, randstr, ip string) error {
c := p.clientConfig(client)
if c.AppID == "" {
return captchaUnavailable()
}
if ticket == "" || len(ticket) > 4096 || strings.HasPrefix(ticket, "trerror") || net.ParseIP(ip) == nil {
return captchaInvalid()
}
id, _ := strconv.ParseUint(c.AppID, 10, 64)
if client == "mini_program" {
r := captcha.NewDescribeCaptchaMiniResultRequest()
r.CaptchaType = common.Uint64Ptr(9)
r.CaptchaAppId = &id
r.AppSecretKey = &c.AppSecretKey
r.Ticket = &ticket
r.UserIp = &ip
out, err := p.api.DescribeCaptchaMiniResultWithContext(ctx, r)
if err != nil {
return captchaUnavailable()
}
if out == nil || out.Response == nil || out.Response.CaptchaCode == nil || *out.Response.CaptchaCode != 1 {
return captchaInvalid()
}
return nil
}
if randstr == "" || len(randstr) > 256 {
return captchaInvalid()
}
r := captcha.NewDescribeCaptchaResultRequest()
r.CaptchaType = common.Uint64Ptr(9)
r.CaptchaAppId = &id
r.AppSecretKey = &c.AppSecretKey
r.Ticket = &ticket
r.Randstr = &randstr
r.UserIp = &ip
out, err := p.api.DescribeCaptchaResultWithContext(ctx, r)
if err != nil {
return captchaUnavailable()
}
if out == nil || out.Response == nil || out.Response.CaptchaCode == nil || *out.Response.CaptchaCode != 1 || (out.Response.EvilLevel != nil && *out.Response.EvilLevel != 0) {
return captchaInvalid()
}
return nil
}
func captchaInvalid() error {
return fail(401, "CAPTCHA_INVALID", "人机验证失败或已过期,请重新验证")
}
func captchaUnavailable() error {
return fail(503, "CAPTCHA_UNAVAILABLE", "人机验证暂不可用,请稍后重试")
}