72 lines
2.1 KiB
Go
72 lines
2.1 KiB
Go
// Package abogus computes the a_bogus signature required by 抖店/抖音 risk-controlled endpoints.
|
||
//
|
||
// Implementation: embeds a JavaScript file with the a_bogus algorithm and runs it via goja
|
||
// (pure-Go JS engine, no CGO). The JS itself does not depend on any browser globals — it
|
||
// takes the URL query string and User-Agent as inputs and returns the signature string.
|
||
package abogus
|
||
|
||
import (
|
||
_ "embed"
|
||
"fmt"
|
||
"sync"
|
||
|
||
"github.com/dop251/goja"
|
||
)
|
||
|
||
//go:embed a_bogus.js
|
||
var aBogusJS string
|
||
|
||
// Generator is reusable. NewGenerator compiles the JS once; Sign() is goroutine-safe.
|
||
type Generator struct {
|
||
program *goja.Program
|
||
mu sync.Mutex
|
||
pool []*goja.Runtime
|
||
}
|
||
|
||
// NewGenerator compiles the embedded a_bogus.js and returns a ready-to-use generator.
|
||
func NewGenerator() (*Generator, error) {
|
||
// strict=false: a_bogus.js 原文有 `n = ...`(未声明)等浏览器宽松模式特性,goja 严格模式会拒绝
|
||
prog, err := goja.Compile("a_bogus.js", aBogusJS, false)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("compile a_bogus.js: %w", err)
|
||
}
|
||
return &Generator{program: prog}, nil
|
||
}
|
||
|
||
// Sign computes the a_bogus signature for a given URL query string and User-Agent.
|
||
// urlSearchParams is the raw query string WITHOUT a_bogus itself (everything else included).
|
||
func (g *Generator) Sign(urlSearchParams, userAgent string) (string, error) {
|
||
rt := g.acquire()
|
||
defer g.release(rt)
|
||
|
||
if _, err := rt.RunProgram(g.program); err != nil {
|
||
return "", fmt.Errorf("run a_bogus.js: %w", err)
|
||
}
|
||
fn, ok := goja.AssertFunction(rt.Get("generate_a_bogus"))
|
||
if !ok {
|
||
return "", fmt.Errorf("generate_a_bogus is not a function")
|
||
}
|
||
v, err := fn(goja.Undefined(), rt.ToValue(urlSearchParams), rt.ToValue(userAgent))
|
||
if err != nil {
|
||
return "", fmt.Errorf("call generate_a_bogus: %w", err)
|
||
}
|
||
return v.String(), nil
|
||
}
|
||
|
||
func (g *Generator) acquire() *goja.Runtime {
|
||
g.mu.Lock()
|
||
defer g.mu.Unlock()
|
||
if n := len(g.pool); n > 0 {
|
||
rt := g.pool[n-1]
|
||
g.pool = g.pool[:n-1]
|
||
return rt
|
||
}
|
||
return goja.New()
|
||
}
|
||
|
||
func (g *Generator) release(rt *goja.Runtime) {
|
||
g.mu.Lock()
|
||
defer g.mu.Unlock()
|
||
g.pool = append(g.pool, rt)
|
||
}
|