diff --git a/core/huya/app_login.py b/core/huya/app_login.py index bcaa1d9..9df356c 100644 --- a/core/huya/app_login.py +++ b/core/huya/app_login.py @@ -833,6 +833,7 @@ class HuyaAppPasswordLogin: code="COOKIE_INCOMPLETE", ) + raw_cookie_str = cookie_str # 补齐前 = App 原始登录凭据(16 字段 udb_*) # 补齐网页设备字段并修正 appId:真实网页登录态是 41 字段 + # udb_appid=5002,App 扫码链只产出 16 个 udb_* 认证字段且 appId=5008 # (App 渠道),缺字段注入浏览器会导致 UDB SDK 判定设备态不完整。 @@ -857,6 +858,7 @@ class HuyaAppPasswordLogin: return HuyaLoginResult( success=True, cookie=cookie_str, + raw_cookie=raw_cookie_str, message="App协议登录成功", sdid=sdid, context=pc.context, diff --git a/core/huya/login.py b/core/huya/login.py index 7edb24e..1094f2c 100644 --- a/core/huya/login.py +++ b/core/huya/login.py @@ -62,6 +62,7 @@ class HuyaLoginResult: success: bool cookie: str = "" + raw_cookie: str = "" # App 原始登录凭据(不含网页设备字段),用于设备态重建/审计 message: str = "" code: int | str = "" raw: dict | None = None diff --git a/tests/test_migrations.py b/tests/test_migrations.py index 61592c9..cdeb8da 100644 --- a/tests/test_migrations.py +++ b/tests/test_migrations.py @@ -22,7 +22,7 @@ TESTS_DIR = Path(__file__).resolve().parent PROJECT_ROOT = TESTS_DIR.parent VERSIONS_DIR = PROJECT_ROOT / "web" / "backend" / "migrations" / "versions" -HEAD_REVISION = "20260901_0033" +HEAD_REVISION = "20260901_0034" # 迁移新增、但不声明在模型里的复合查询索引。 EXTRA_INDEXES = ( diff --git a/web/backend/migrations/versions/20260901_0034_huya_accounts_raw_cookie.py b/web/backend/migrations/versions/20260901_0034_huya_accounts_raw_cookie.py new file mode 100644 index 0000000..778dacd --- /dev/null +++ b/web/backend/migrations/versions/20260901_0034_huya_accounts_raw_cookie.py @@ -0,0 +1,40 @@ +"""huya_accounts 增加 raw_cookie:保存 App 登录原始凭据(与网页运行 ck 分离)。 + +网页运行 ck(cookie 列)含本地随机生成的网页设备字段(guid/_qimei_uuid42/ +__yamid_new/game_did),若设备字段被风控标记无法还原纯净的登录凭据。 +raw_cookie 保存 16 字段的原始 App 登录态(udb_* 认证 + sdid/hdid + 匿名设备 +字段),用于重建设备态/审计/复核登录有效性。 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +revision: str = "20260901_0034" +down_revision: str | None = "20260901_0033" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + bind = op.get_bind() + columns = { + item["name"] for item in sa.inspect(bind).get_columns("huya_accounts") + } + if "raw_cookie" not in columns: + # MySQL 的 TEXT 列不允许 server_default;模型层 default="" 负责新行, + # 存量行为 NULL(读取处按空处理)。 + op.add_column( + "huya_accounts", + sa.Column("raw_cookie", sa.Text(), nullable=True), + ) + + +def downgrade() -> None: + bind = op.get_bind() + columns = { + item["name"] for item in sa.inspect(bind).get_columns("huya_accounts") + } + if "raw_cookie" in columns: + op.drop_column("huya_accounts", "raw_cookie") \ No newline at end of file diff --git a/web/backend/models.py b/web/backend/models.py index 96bd7dd..499c7f0 100644 --- a/web/backend/models.py +++ b/web/backend/models.py @@ -369,6 +369,10 @@ class HuyaAccount(Base): account_password: Mapped[str] = mapped_column(EncryptedText(), default="") nickname: Mapped[str] = mapped_column(String(128), default="") cookie: Mapped[str] = mapped_column(EncryptedText(), nullable=False) + # App 登录原始凭据(16 字段 udb_*,不含本地随机生成的网页设备字段)。 + # 网页运行 ck(cookie 列)含 guid/_qimei_uuid42 等设备快照,若被风控标记 + # 可从 raw_cookie 重建;也用于审计/复核登录有效性。 + raw_cookie: Mapped[str] = mapped_column(EncryptedText(), nullable=False, default="") tag: Mapped[str] = mapped_column(String(64), default="") remark: Mapped[str] = mapped_column(String(256), default="") status: Mapped[str] = mapped_column(String(32), default="imported") diff --git a/web/backend/routers/huya.py b/web/backend/routers/huya.py index 6f38675..4256781 100644 --- a/web/backend/routers/huya.py +++ b/web/backend/routers/huya.py @@ -695,7 +695,11 @@ def app_password_login_account( try: account = upsert_huya_cookie( - db, result.cookie, tag=req.tag, username_hint=req.username + db, + result.cookie, + tag=req.tag, + username_hint=req.username, + raw_cookie=getattr(result, "raw_cookie", ""), ) if hasattr(account, "account_password") and req.password: account.account_password = req.password diff --git a/web/backend/services/huya_service.py b/web/backend/services/huya_service.py index dd939ac..f75d3fa 100644 --- a/web/backend/services/huya_service.py +++ b/web/backend/services/huya_service.py @@ -303,9 +303,18 @@ def save_huya_login_cookie_to_account( def upsert_huya_cookie( - db: Session, cookie: str, tag: str = "", username_hint: str = "" + db: Session, + cookie: str, + tag: str = "", + username_hint: str = "", + raw_cookie: str = "", ) -> HuyaAccount: - """保存单条登录得到的虎牙 Cookie。""" + """保存单条登录得到的虎牙 Cookie。 + + Args: + cookie: 网页运行 Cookie(可能含本地生成的网页设备字段)。 + raw_cookie: App 原始登录凭据(不含网页设备字段),单独落库供重建/审计。 + """ line = f"{username_hint}----{cookie}" if username_hint else cookie parsed = parse_huya_cookie_line(line) if not parsed: @@ -313,6 +322,8 @@ def upsert_huya_cookie( account = _upsert_huya_account( db, parsed, tag=(tag or "").strip(), status="login_success" ) + if raw_cookie: + account.raw_cookie = normalize_huya_cookie(raw_cookie) db.commit() db.refresh(account) return account