- 新增 /rank 免签名榜单接口(多域名容错)、/bookshelf 与 /read-history(sessionid 登录态,书架支持分组过滤) - 搜索重写:翻页透传上游 passback/search_id/next_offset/corrected_query 并按 book_id 去重,15~20 位纯数字关键词直查详情(ID 直搜) - 删除 /login/check 登录校验端点(login.go、路由与文档同步移除,sessionid 说明改为直接供书架/历史使用) - 正文链路重构:singleflight 合并同章节并发回源防击穿,缓存与解析解耦统一在出口处理,decodeContentBody 按 crypt_status 分支并兜底 500003 明文 - 缓存层:SQL 持久层 TTL 与业务 TTL 解耦(不可变内容过期后由 SQL 回填),动态短缓存只走 Redis,withCache 合并并发 miss - 密钥池:KeyTs 随正文请求上传降低密钥不匹配重试,密钥超 24 小时异步刷新并防刷新风暴,last_used_at 批量落库 - 签名补 X-Helios(与 X-Ladon 同值,对齐书源实现);设备注册版本号统一走 ClientVersionCode/Name - 分类 landing 透传新版标签 selected_items;multi-detail 批量详情供书架/历史复用 - README 与 openapi.yaml 补充新接口文档
221 lines
5.9 KiB
Go
221 lines
5.9 KiB
Go
package algos
|
||
|
||
import (
|
||
"fmt"
|
||
"net/url"
|
||
"regexp"
|
||
"strconv"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// getTimestamp 获取时间戳(辅助函数)
|
||
func getTimestamp(unix *int64) int64 {
|
||
if unix == nil {
|
||
return time.Now().Unix()
|
||
}
|
||
return *unix
|
||
}
|
||
|
||
// Sign2 签名方法2(Gorgon 或 8404)
|
||
func Sign2(
|
||
params string,
|
||
xSSStub string,
|
||
secDeviceID string,
|
||
cookie string,
|
||
aid int,
|
||
licenseID int,
|
||
sdkVersionStr string,
|
||
sdkVersion int,
|
||
platform int,
|
||
unix *int64,
|
||
) map[string]string {
|
||
timestamp := getTimestamp(unix)
|
||
|
||
// 根据当前算法选择生成签名
|
||
currentAlgo := GlobalAlgorithmSwitcher.GetCurrentAlgorithm()
|
||
if currentAlgo == "8404" {
|
||
algo := NewAlgo8404(params, timestamp, xSSStub)
|
||
return algo.GetValue()
|
||
}
|
||
|
||
// 默认使用 Gorgon
|
||
gorgon := NewGorgon(params, timestamp, xSSStub, cookie)
|
||
return gorgon.GetValue()
|
||
}
|
||
|
||
// Sign4 签名方法4(Gorgon + Ladon + Argus)
|
||
func Sign4(
|
||
params string,
|
||
xSSStub string,
|
||
secDeviceID string,
|
||
cookie string,
|
||
aid int,
|
||
licenseID int,
|
||
sdkVersionStr string,
|
||
sdkVersion int,
|
||
platform int,
|
||
unix *int64,
|
||
) map[string]string {
|
||
timestamp := getTimestamp(unix)
|
||
|
||
// 根据当前算法选择生成基础签名
|
||
var result map[string]string
|
||
currentAlgo := GlobalAlgorithmSwitcher.GetCurrentAlgorithm()
|
||
if currentAlgo == "8404" {
|
||
algo := NewAlgo8404(params, timestamp, xSSStub)
|
||
result = algo.GetValue()
|
||
} else {
|
||
// 默认使用 Gorgon
|
||
gorgon := NewGorgon(params, timestamp, xSSStub, cookie)
|
||
result = gorgon.GetValue()
|
||
}
|
||
|
||
// 添加 Ladon 和 Argus 签名
|
||
ladon := &Ladon{}
|
||
result["x-ladon"] = ladon.Encrypt(timestamp, licenseID, aid)
|
||
result["x-helios"] = result["x-ladon"] // X-Helios 与 X-Ladon 同值(对齐书源实现)
|
||
|
||
argus := &Argus{}
|
||
result["x-argus"] = argus.GetSign(
|
||
params,
|
||
xSSStub,
|
||
timestamp,
|
||
aid,
|
||
licenseID,
|
||
platform,
|
||
secDeviceID,
|
||
sdkVersionStr,
|
||
sdkVersion,
|
||
)
|
||
|
||
return result
|
||
}
|
||
|
||
// SignatureInterface 签名接口
|
||
type SignatureInterface interface {
|
||
Sign(params, xSSStub, secDeviceID, cookie string, aid, licenseID int, sdkVersionStr string, sdkVersion, platform int, unix *int64) map[string]string
|
||
}
|
||
|
||
// SignatureSwitcher 签名方法切换器
|
||
type SignatureSwitcher struct {
|
||
mu sync.RWMutex
|
||
currentMethod string
|
||
methods map[string]SignatureInterface
|
||
}
|
||
|
||
// NewSignatureSwitcher 创建签名切换器
|
||
func NewSignatureSwitcher() *SignatureSwitcher {
|
||
switcher := &SignatureSwitcher{
|
||
methods: make(map[string]SignatureInterface),
|
||
}
|
||
|
||
// 注册所有签名方法
|
||
switcher.methods["sign2"] = &Sign2Method{}
|
||
switcher.methods["sign4"] = &Sign4Method{}
|
||
|
||
// 设置默认签名方法为sign2
|
||
switcher.currentMethod = "sign2"
|
||
|
||
return switcher
|
||
}
|
||
|
||
// SwitchSignature 切换签名方法
|
||
func (s *SignatureSwitcher) SwitchSignature(method string) error {
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
|
||
if _, exists := s.methods[method]; !exists {
|
||
return fmt.Errorf("不支持的签名方法: %s", method)
|
||
}
|
||
|
||
s.currentMethod = method
|
||
return nil
|
||
}
|
||
|
||
// GetCurrentMethod 获取当前签名方法
|
||
func (s *SignatureSwitcher) GetCurrentMethod() string {
|
||
s.mu.RLock()
|
||
defer s.mu.RUnlock()
|
||
return s.currentMethod
|
||
}
|
||
|
||
// Sign 使用当前签名方法进行签名
|
||
func (s *SignatureSwitcher) Sign(urlStr string, headers map[string]string) map[string]string {
|
||
return SignFromURL(s.GetCurrentMethod(), urlStr, headers)
|
||
}
|
||
|
||
// SignFromParams 使用当前签名方法进行签名
|
||
func (s *SignatureSwitcher) SignFromParams(params, xSSStub, secDeviceID, cookie string, aid, licenseID int, sdkVersionStr string, sdkVersion, platform int, unix *int64) map[string]string {
|
||
s.mu.RLock()
|
||
method := s.methods[s.currentMethod]
|
||
s.mu.RUnlock()
|
||
|
||
return method.Sign(params, xSSStub, secDeviceID, cookie, aid, licenseID, sdkVersionStr, sdkVersion, platform, unix)
|
||
}
|
||
|
||
// Sign2Method Sign2签名方法实现
|
||
type Sign2Method struct{}
|
||
|
||
func (s *Sign2Method) Sign(params, xSSStub, secDeviceID, cookie string, aid, licenseID int, sdkVersionStr string, sdkVersion, platform int, unix *int64) map[string]string {
|
||
return Sign2(params, xSSStub, secDeviceID, cookie, aid, licenseID, sdkVersionStr, sdkVersion, platform, unix)
|
||
}
|
||
|
||
// Sign4Method Sign4签名方法实现
|
||
type Sign4Method struct{}
|
||
|
||
func (s *Sign4Method) Sign(params, xSSStub, secDeviceID, cookie string, aid, licenseID int, sdkVersionStr string, sdkVersion, platform int, unix *int64) map[string]string {
|
||
return Sign4(params, xSSStub, secDeviceID, cookie, aid, licenseID, sdkVersionStr, sdkVersion, platform, unix)
|
||
}
|
||
|
||
// GlobalSignatureSwitcher 全局签名切换器实例
|
||
var GlobalSignatureSwitcher = NewSignatureSwitcher()
|
||
|
||
// SignFromURL 从 URL 和 headers 中提取参数并生成签名
|
||
func SignFromURL(method, urlStr string, headers map[string]string) map[string]string {
|
||
// 解析 URL
|
||
u, err := url.Parse(urlStr)
|
||
if err != nil {
|
||
return make(map[string]string)
|
||
}
|
||
|
||
// 提取查询参数
|
||
params := u.RawQuery
|
||
xSSStub := headers["x-ss-stub"]
|
||
cookie := headers["Cookie"]
|
||
|
||
// 从 URL 中提取 device_id 和 aid
|
||
deviceIDRegex := regexp.MustCompile(`device_id=(\d+)`)
|
||
aidRegex := regexp.MustCompile(`aid=(\d+)`)
|
||
|
||
deviceIDMatch := deviceIDRegex.FindStringSubmatch(urlStr)
|
||
aidMatch := aidRegex.FindStringSubmatch(urlStr)
|
||
|
||
secDeviceID := ""
|
||
aid := 1967
|
||
if len(deviceIDMatch) > 1 {
|
||
secDeviceID = deviceIDMatch[1]
|
||
}
|
||
if len(aidMatch) > 1 {
|
||
aid, _ = strconv.Atoi(aidMatch[1])
|
||
}
|
||
|
||
// 使用固定参数
|
||
licenseID := 1611921764
|
||
sdkVersionStr := "v04.06.01-ml-android"
|
||
sdkVersion := 135004672
|
||
platform := 0
|
||
|
||
// 根据方法调用对应的签名函数
|
||
if method == "sign4" {
|
||
return Sign4(params, xSSStub, secDeviceID, cookie, aid, licenseID, sdkVersionStr, sdkVersion, platform, nil)
|
||
}
|
||
return Sign2(params, xSSStub, secDeviceID, cookie, aid, licenseID, sdkVersionStr, sdkVersion, platform, nil)
|
||
}
|
||
|
||
// Sign2Wrapper 硬编码使用 Sign2(从 URL 和 headers 生成签名)
|
||
func Sign2Wrapper(urlStr string, headers map[string]string) map[string]string {
|
||
return SignFromURL("sign2", urlStr, headers)
|
||
}
|
||
|