Go 服务端 SDK
官方 Go 服务端 SDK —— github.com/Captcha-La/captchala-go。
SDK 帮你做三件事:
- 校验(Validate)—— 验证浏览器 SDK 返回的
pt_pass token。 - 签发(Issue)—— 签发一次性
sct_server token, 将本次挑战绑定到具体 action / IP / UID。 - 审核(Moderate)—— 多模态(文本 + 图片)内容审核, 走 dashboard 同款 OpenAI 兼容管道。
安装
bash
go get github.com/Captcha-La/captchala-go只用标准库, 无外部依赖。
校验(pt_ token)
传入终端用户的 IP(从 CF-Connecting-IP / X-Forwarded-For 获取, 回退到 RemoteAddr)。可选,但建议传 —— 用于额外的风控校验。
go
import captchala "github.com/Captcha-La/captchala-go"
client := captchala.NewClient(appKey, appSecret)
func handler(w http.ResponseWriter, r *http.Request) {
token := r.FormValue("captcha_token")
result, err := client.ValidateWithClientIP(token, false, clientIP(r))
if err != nil || !result.Valid {
http.Error(w, result.Error, http.StatusBadRequest)
return
}
// Verification passed; proceed with the request.
// result.CaptchaArgs has Platform / UserIP / Referer / Pkg / SolvedAt / RiskScore
}
// Real end-user IP behind a CDN / proxy
func clientIP(r *http.Request) string {
if ip := r.Header.Get("CF-Connecting-IP"); ip != "" {
return ip
}
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
return strings.TrimSpace(strings.Split(xff, ",")[0])
}
host, _, _ := net.SplitHostPort(r.RemoteAddr)
return host
}手头没有 IP?用 client.Validate(token) —— 校验照常工作, 只是少了基于 IP 的 额外风控信号。
如果原 server_token 带了 bind_uid, 校验时核对 uid:
go
if result.UID != expectedUserID {
http.Error(w, "user mismatch", http.StatusBadRequest)
return
}解题时的上下文也通过 result.CaptchaArgs 返回供参考:
go
ip := result.CaptchaArgs.UserIP // 解题时记录的访客 IP签发 server token
注册 / 登录 / 支付等高价值场景推荐流程:后端签发一次性 sct_ token, 下发给前端, 浏览器作为 serverToken 用。单次消费, 绑定 action, 可选绑定 IP / UID。
go
issue, err := client.IssueServerTokenWithOptions("login", captchala.IssueOptions{
BindingIP: userIP, // 不同 IP 来兑换会被拒
TTL: 300, // 秒
MaxUses: 5, // SDK 重试预算
BindUID: userID, // 配合 ValidateResult.UID 校验
})
if err != nil || !issue.OK {
http.Error(w, issue.Error, http.StatusBadRequest)
return
}
// 把 issue.Token 下发给浏览器默认参数快捷方式:
go
issue, err := client.IssueServerToken("login")内容审核
多模态 —— 接受 ModerationItem 切片(文本 / image_url 混合, OpenAI 兼容):
go
result, err := client.ModerationCheck([]captchala.ModerationItem{
captchala.TextItem(userComment),
captchala.ImageURLItem(uploadedImageURL),
}, userID)
if err != nil || !result.OK {
http.Error(w, result.Error, http.StatusBadRequest)
return
}
if result.Flagged {
if result.HasCategory("violence", "csam") {
// 硬阻断
}
}纯文本快捷方式:
go
result, err := client.ModerationText("user comment here", userID)Category 列表由上游模型决定, 用 result.Categories 防御性遍历, 不要硬编码固定集合。
类型
| 类型 | 字段 |
|---|---|
ValidateResult | Valid、Error、UID、ChallengeID、Action、Offline、ClientOnly、Warning、CaptchaArgs |
CaptchaArgs | Platform、UserIP、Referer、Pkg、SolvedAt、RiskScore(纯信息字段, 切勿基于这些放行) |
IssueResult | OK、Token、ExpiresIn、IssuedAt、Error、Message |
IssueOptions | BindingIP、TTL、MaxUses、BindUID |
ModerationItem | Type、Text、ImageURL(用 TextItem() / ImageURLItem() 辅助函数构造) |
ModerationResult | OK、Flagged、Categories、ContentType、Raw、Error、Message;方法 HasCategory(...names) |