106 lines
3.4 KiB
Go
106 lines
3.4 KiB
Go
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", "人机验证暂不可用,请稍后重试")
|
||
|
|
}
|