Files
chenxin 84bd3238ab ✨ feat(api): 新增榜单书架历史与搜索续传并重构正文缓存链路
- 新增 /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 补充新接口文档
2026-10-12 14:49:14 +08:00

221 lines
5.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}