news 2026/9/19 13:03:24

Tampermonkey脚本实现共享账号Cookie自动注入与登录态维护

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Tampermonkey脚本实现共享账号Cookie自动注入与登录态维护

1. 这个脚本到底解决了什么问题

第一次看到“Exhentai-Shared-Account”这个标题,很多人脑子里冒出来的第一个词大概是“共享账号”。没错,它的核心逻辑就是字面意思——把一组公共可用的登录凭据,通过浏览器脚本的方式自动写入到目标站点的 Cookie 里,让访问者不需要手动输入账号密码,就能直接进入里站浏览内容。整个项目本质上是一个跑在 Tampermonkey(篡改猴)里的 JavaScript 脚本,依赖浏览器扩展注入页面,操作对象是 Cookie。

先把话说在前面:这篇文章只讨论脚本本身的技术实现思路、Cookie 机制的原理、以及这类“共享凭据自动注入”方案在工程上会遇到哪些坑。至于账号从哪来、共享行为是否合规、目标站点的服务条款怎么规定,这些不在技术讨论范围内,需要读者自行判断。我写这篇东西的出发点很简单——这类脚本涉及的知识点其实相当密集:Cookie 的作用域与生命周期、Tampermonkey 的注入时机、跨子域写入的限制、脚本被浏览器安全策略拦截的排查方法。把这些讲透,比单纯丢一个安装链接有价值得多。

适合谁来读?如果你满足下面任意一条,这篇内容对你就有用:

  • 你写过或想写 Tampermonkey 脚本,但对@match@run-atGM_cookie这些指令的实际行为一知半解;
  • 你遇到过“脚本装了但没生效”“Cookie 写进去了但刷新就没了”“登录状态时有时无”这类问题,想搞清楚底层原因;
  • 你想理解浏览器 Cookie 的 Domain、Path、SameSite、HttpOnly 这几个属性到底怎么影响脚本操作;
  • 你对“共享凭据自动注入”这种模式的技术架构感兴趣,想评估它的稳定性和风险点。

我前后折腾过好几个版本的类似脚本,踩过的坑包括但不限于:Cookie 写入了但 Domain 不匹配导致浏览器直接丢弃、document.cookie在 HttpOnly 面前完全失效、Tampermonkey 的沙箱环境和页面环境对 Cookie 的可见性不一致、脚本执行时机太早导致页面已经发起了未携带凭据的请求。这些问题在官方文档里往往一笔带过,但在实际使用中每一个都能让你卡上半天。下面我把整个方案拆开,从设计思路到实操细节,再到排查手册,尽量讲清楚。

2. 脚本整体设计与核心思路拆解

2.1 为什么选择 Tampermonkey 而不是独立程序

这类“自动登录/凭据注入”需求,技术上至少有四种实现路径:浏览器扩展、独立桌面程序、命令行工具配合系统级 Cookie 数据库操作、以及用户脚本(Userscript)。这个项目选了用户脚本,背后是有明确取舍的。

独立桌面程序(比如用 Python + Selenium)的问题是重——你得装运行环境、装浏览器驱动、每次启动要等浏览器实例起来,而且它操作的是一个全新的浏览器会话,跟你日常用的浏览器完全隔离,Cookie 不共享。命令行工具直接改浏览器 Cookie 数据库(Chrome 的CookiesSQLite 文件)看起来优雅,但 Chrome 在运行时会锁住这个文件,你还得先关浏览器,而且新版 Chrome 对 Cookie 值做了加密(Windows 上绑定 DPAPI,macOS 上绑定 Keychain),解密成本很高,跨平台极不友好。

浏览器扩展能做到最深度的控制,但开发、打包、上架、维护一套流程下来,为了一个“写几个 Cookie”的需求实在不划算。用户脚本正好卡在中间:它借助 Tampermonkey 这个宿主扩展获得了接近原生扩展的 API 能力(比如GM_cookieGM_setValue),同时开发成本极低——一个.user.js文件,改完刷新页面就生效,不需要打包和签名。

提示:Tampermonkey 提供的GM_cookieAPI 是这类脚本能绕开document.cookie限制的关键。原生document.cookie无法读写 HttpOnly 的 Cookie,也无法跨域操作,而GM_cookie在用户授权后可以做到这两点。这是选型时最容易被忽略、但实际最要命的一个差异点。

2.2 核心流程:从脚本注入到凭据生效

整个脚本的工作流可以拆成五个阶段,理解这五个阶段是排查一切问题的前提:

  1. 匹配与注入:Tampermonkey 根据脚本头部的@match@include规则,判断当前页面 URL 是否命中,命中则在指定时机(@run-at)把脚本注入页面。
  2. 凭据读取:脚本从内置的常量、远程配置接口、或者GM_setValue存储中拿到那组共享账号对应的 Cookie 键值对。
  3. Cookie 写入:通过GM_cookie.setdocument.cookie把键值对写入目标域。
  4. 状态校验:写入后发起一次探测请求(或读取页面特征元素),确认登录态是否真的生效。
  5. 失败重试与降级:如果校验失败,脚本可能尝试备用凭据、调整写入参数、或提示用户手动干预。

这五步里,第三步和第四步是绝大多数“脚本不生效”问题的根源。写入这一步,很多人以为document.cookie = "key=value"就完事了,实际上浏览器会根据当前页面的域、路径、协议、以及 Cookie 自身的属性做一系列校验,任何一项不匹配都会静默丢弃——注意是静默,浏览器不会报错,你只会发现“怎么没写进去”。

2.3 共享凭据模式固有的稳定性难题

必须承认,“共享账号”这个模式在工程上有天然缺陷,理解这些缺陷能帮你建立合理的预期,也能帮你在脚本设计时做出更稳健的取舍。

第一个难题是凭据的时效性。共享账号的密码或会话令牌可能被服务端定期轮换、被其他使用者触发风控、或者因为并发登录数超限而被临时封禁。脚本本身无法创造有效的凭据,它只能搬运。所以这类脚本的可用性高度依赖凭据源的维护频率。

第二个难题是并发冲突。同一个账号被多人同时使用时,服务端可能检测到异常(比如同一账号在短时间内从多个地理位置发起请求),从而触发验证或封禁。脚本层面能做的缓解很有限,通常只能靠“错峰”或者“多凭据轮换”来降低单账号压力。

第三个难题是Cookie 与登录态的绑定关系。很多站点不是简单校验一个 Cookie 值,而是把会话 ID 和服务端的会话记录绑定。你光有 Cookie 字符串还不够,服务端那边的会话记录必须还有效。这就解释了为什么有时候“Cookie 明明写对了,但还是未登录”——服务端会话已经过期了。

理解了这三点,你就明白为什么这类脚本的 README 里总会写“不保证长期可用”。这不是作者偷懒,而是模式本身的限制。

3. Cookie 机制与脚本注入的关键细节

3.1 Cookie 的五个核心属性,一个都不能错

要让脚本写入的 Cookie 真正生效,必须同时满足五个属性的约束。我用一个表格把它们列清楚,这是排查问题的第一手资料:

属性作用脚本操作时的常见错误
Domain指定 Cookie 对哪些域可见写成www.example.com但目标请求发往example.com,导致不匹配
Path指定 Cookie 对哪些路径可见写成/forum但实际请求路径是/,Cookie 不携带
Expires/Max-Age过期时间不设置则成为会话 Cookie,关浏览器即失效
Secure仅通过 HTTPS 传输在 HTTP 页面写入 Secure Cookie 会被拒绝
SameSite跨站请求是否携带设为Strict时,从外部链接跳转进来首次请求不带 Cookie

这里最容易翻车的是 Domain。假设目标站点是exhentai.org,但登录接口实际在forums.exhentai.org,那么 Cookie 的 Domain 必须写成.exhentai.org(前面带点表示包含所有子域),而不是exhentai.orgforums.exhentai.org。写错了,浏览器在发送请求时就会认为“这个 Cookie 不属于当前请求的域”,直接不携带。

注意:Domain 前面那个点在现代浏览器里的语义已经弱化了。RFC 6265 规定,Domain=.example.comDomain=example.com在大多数情况下等价,都会匹配所有子域。但为了兼容老代码和避免歧义,很多脚本仍然保留前导点。真正会导致失败的是把 Domain 写成某个具体子域,却期望它在另一个子域生效。

3.2 document.cookie 与 GM_cookie 的能力边界

这是脚本开发里最需要搞清楚的一组对比。很多人写脚本时习惯性用document.cookie,结果遇到 HttpOnly 就彻底没辙。

document.cookie的能力边界:

  • 只能读写当前页面域下的 Cookie,无法跨域;
  • 无法读取HttpOnly标记的 Cookie(这是浏览器故意的安全设计,防止 XSS 窃取会话);
  • 写入时受当前页面的协议、路径限制;
  • 读取时返回的是所有非 HttpOnly Cookie 拼接成的字符串,需要自己解析。

GM_cookie的能力边界(需要 Tampermonkey 授权):

  • 可以读写任意域的 Cookie,只要用户在 Tampermonkey 设置里授予了权限;
  • 可以读取HttpOnlyCookie(这是它最大的价值);
  • 提供结构化的listsetdelete接口,不用自己解析字符串;
  • 但它是异步 API,必须用回调或 Promise 处理,写起来比document.cookie啰嗦。

对于“共享账号注入”这个场景,如果目标站点的会话 Cookie 是 HttpOnly 的(绝大多数正规站点都是),那么document.cookie方案从根上就行不通,必须用GM_cookie。这也是为什么很多早期版本的脚本在站点更新后就失效了——站点把会话 Cookie 改成了 HttpOnly,脚本的写入方式没跟着变。

3.3 脚本注入时机:@run-at 的选择逻辑

Tampermonkey 的@run-at指令决定了脚本在页面生命周期的哪个点执行,常见取值有:

  • document-start:DOM 还没开始构建,页面脚本尚未执行;
  • document-end:DOM 构建完成,但外部资源(图片、样式)可能还在加载;
  • document-idle:页面完全加载完毕,所有资源就绪。

对于凭据注入类脚本,理论上越早越好——因为如果页面在脚本写入 Cookie 之前就发起了登录校验请求,那次请求就是未登录状态,页面可能已经渲染成“未登录”的样子了。所以理想选择是document-start

document-start有个坑:此时GM_cookie的异步写入可能还没完成,页面脚本就已经跑了。这就产生了一个竞态条件。稳健的做法是:在document-start阶段同步地拦截关键请求(如果脚本有能力的话),或者接受“首次加载可能未登录,写入完成后自动刷新一次”的降级方案。很多成熟脚本采用的就是“写入后检测,未生效则location.reload()”的策略。

提示:如果你发现脚本“第一次打开没登录,刷新一下就好了”,基本可以确定是注入时机和异步写入的竞态问题。这不是 bug,而是这类方案的固有特性,可以通过在脚本里主动触发一次刷新来改善体验。

4. 实操过程与核心环节实现

4.1 环境准备与脚本安装

先把基础环境搭起来。整个过程分三步,我按实际操作顺序写:

  1. 安装浏览器扩展:在 Chrome、Edge、Firefox 等主流浏览器的扩展商店里搜索 Tampermonkey(中文名“篡改猴”),安装并启用。安装后浏览器工具栏会出现一个黑色图标,点击能看到管理面板。
  2. 开启开发者模式:部分浏览器(尤其是 Chrome 的新版本)需要在扩展管理页面打开右上角的“开发者模式”,否则 Tampermonkey 无法正常注入脚本。这一步经常被忽略,导致“脚本装了但完全没反应”。
  3. 导入脚本:打开 Tampermonkey 管理面板,选择“添加新脚本”,把.user.js的内容粘贴进去,或者直接把.user.js文件拖进浏览器窗口,Tampermonkey 会自动识别并弹出安装确认页。

安装确认页会列出脚本声明的权限,比如GM_cookieGM_setValue、以及@match的域名列表。这里要仔细看一眼——如果脚本声明了GM_cookie但你没在 Tampermonkey 的“设置 → 高级 → 安全”里允许相应权限,脚本运行时会拿不到 Cookie 读写能力。

4.2 脚本头部配置的逐行解读

一个典型的凭据注入脚本,头部大概长这样(这是基于常见实践的示例结构,不是某个具体脚本的原文):

// ==UserScript== // @name Shared Account Auto Login // @namespace local.shared.login // @version 1.0.0 // @description 自动注入共享凭据 // @match https://target-site.example/* // @grant GM_cookie // @grant GM_setValue // @grant GM_getValue // @run-at document-start // @connect config-source.example // ==/UserScript==

逐行说明关键项:

  • @match:决定脚本在哪些页面注入。注意https://target-site.example/*只匹配该域下的所有路径,但不匹配子域。如果要覆盖子域,得写成https://*.target-site.example/*
  • @grant:声明需要使用的 Tampermonkey 特权 API。每用一个都要声明,漏声明会导致该 API 在脚本里是undefined
  • @run-at document-start:尽早注入,理由前面讲过。
  • @connect:如果脚本要从远程拉取凭据配置,必须在这里声明目标域名,否则 Tampermonkey 会拦截跨域请求。

4.3 Cookie 写入的核心代码与参数计算

写入环节是整个脚本的心脏。用GM_cookie写入的典型代码结构如下:

function injectCookies(cookieList) { return new Promise((resolve, reject) => { let pending = cookieList.length; if (pending === 0) return resolve(); cookieList.forEach(function (item) { GM_cookie.set({ name: item.name, value: item.value, domain: item.domain, // 例如 ".target-site.example" path: item.path || "/", secure: true, httpOnly: item.httpOnly || false, expirationDate: item.expirationDate || (Date.now() / 1000 + 86400 * 7) }, function (err) { if (err) console.error("写入失败:", item.name, err); pending--; if (pending === 0) resolve(); }); }); }); }

几个参数的计算和选择逻辑值得展开:

expirationDate 的取值。这个字段是 Unix 时间戳(秒),不是毫秒。Date.now()返回的是毫秒,所以必须除以 1000。上面代码里86400 * 7表示 7 天。为什么设 7 天而不是永久?因为共享凭据本身可能几天就失效了,设太长没意义,反而可能在凭据失效后留下一个“看起来还在但实际无效”的 Cookie,干扰排查。设太短又会导致频繁重新注入。7 天是个经验值,你可以根据凭据源的更新频率调整。

domain 的确定方法。不要凭感觉写。正确做法是:在目标站点正常登录一次,打开浏览器开发者工具的“应用 → Cookie”面板,看真实会话 Cookie 的 Domain 字段是什么,照着抄。如果真实 Cookie 的 Domain 是.target-site.example,你就写.target-site.example;如果它没有 Domain(表示仅当前精确域),你就留空或写当前域。

httpOnly 的取舍。如果目标站点的会话 Cookie 是 HttpOnly 的,你写入时也应该设httpOnly: true,否则可能出现“脚本写的 Cookie 和站点期望的 Cookie 属性不一致”导致的行为异常。但要注意,设了 HttpOnly 之后,你自己用document.cookie就读不到了,调试时得用GM_cookie.list

4.4 写入后的校验与自动刷新

写完 Cookie 不代表登录态就生效了。稳健的脚本会做一次校验,我常用的校验逻辑有两种:

第一种是特征元素检测。登录后页面通常会显示用户名、头像、或者某个只有登录用户才能看到的按钮。脚本在写入 Cookie 后等待一段时间(比如 1.5 秒),然后查询这个特征元素是否存在。存在则说明成功,不存在则触发刷新或提示。

function verifyLogin() { const marker = document.querySelector(".user-avatar, .logout-link"); return !!marker; } setTimeout(function () { if (!verifyLogin()) { console.warn("登录态未生效,尝试刷新"); location.reload(); } }, 1500);

第二种是探测请求。用fetch请求一个只有登录用户才能访问的接口,看返回状态码。这种方式更准确,但需要知道具体的接口地址,且可能触发额外的服务端日志。

注意:自动刷新要加防抖。如果凭据本身已经失效,脚本会陷入“写入 → 校验失败 → 刷新 → 再写入 → 再失败”的死循环。务必用sessionStorageGM_setValue记录刷新次数,超过阈值就停止并提示用户。

5. 常见问题与排查技巧实录

5.1 脚本完全不生效的排查顺序

遇到“装了脚本但页面毫无变化”,按下面这个顺序排查,基本能定位到问题:

排查项检查方法典型原因
脚本是否注入打开控制台看有没有脚本的日志输出@match规则不匹配当前 URL
权限是否授予Tampermonkey 面板看脚本的权限状态GM_cookie未授权
开发者模式浏览器扩展页看开关Chrome 新版默认关闭
注入时机看日志时间戳与页面加载的关系@run-at太晚,页面已渲染
Cookie 是否写入GM_cookie.list打印当前 CookieDomain/Path 不匹配被丢弃

我踩过最隐蔽的一个坑是@match的协议问题。脚本写的是http://,但站点已经全站跳转https://,结果脚本永远不注入。这种问题在控制台里没有任何报错,只能靠肉眼比对 URL。

5.2 Cookie 写入成功但登录态不生效

这是最高频的问题,原因通常有三类:

第一类:服务端会话已失效。Cookie 字符串本身是有效的格式,但服务端对应的会话记录已经过期或被清理。这种情况下,无论你怎么写 Cookie 都没用,只能换一组凭据。判断方法:用同样的 Cookie 在无痕窗口手动测试,如果手动也不生效,就是凭据问题,不是脚本问题。

第二类:Cookie 不完整。很多站点的登录态不止一个 Cookie,可能是session_id+csrf_token+user_prefs的组合。脚本只写了其中一个,自然不生效。解决办法是抓取一次完整登录后的所有 Cookie,全部注入。

第三类:属性不匹配。前面讲过的 Domain、Path、Secure、SameSite 任何一个不对都会导致 Cookie 不被携带。用开发者工具的“网络”面板看实际请求头里的Cookie字段,对比你写入的 Cookie,就能发现差异。

5.3 共享凭据的稳定性维护经验

用了这类脚本一段时间后,我总结出几条维护经验,都是实打实踩出来的:

  • 准备多组备用凭据。脚本里内置一个凭据数组,主凭据校验失败时自动切换到备用。这能显著提升可用性,因为单组凭据的失效是常态。
  • 记录失效时间点。用GM_setValue记录每次凭据失效的时间,观察规律。如果发现某组凭据总是在固定时间失效,可能是服务端的定时清理策略,可以据此调整更新频率。
  • 不要在高峰期使用。共享账号的并发压力在特定时段会很高,触发风控的概率也大。错峰使用能降低被临时限制的风险。
  • 定期清理残留 Cookie。凭据切换时,旧凭据的 Cookie 如果没清干净,可能和新凭据冲突。切换前先用GM_cookie.delete清一遍目标域的相关 Cookie。

5.4 浏览器安全策略带来的额外限制

现代浏览器对第三方 Cookie 的限制越来越严,这对脚本有直接影响。如果你的脚本需要在 A 站点写入 B 站点的 Cookie(跨站场景),可能被浏览器的“阻止第三方 Cookie”策略拦截。Tampermonkey 的GM_cookie在一定程度上能绕过这个限制,但前提是用户授予了相应权限,且浏览器的“隐私沙盒”相关设置没有把它彻底堵死。

另外,Chrome 的“增强型安全浏览”和某些企业策略会限制用户脚本的执行。如果你在公司电脑上发现脚本怎么都不生效,先确认是不是被组策略限制了。

6. 我对这类方案的真实看法

折腾了这么多版本,我最深的体会是:这类脚本的技术门槛其实不高,难的是对 Cookie 机制的理解深度和排查问题的耐心。真正让人卡住的从来不是“怎么写代码”,而是“为什么写了没生效”——而答案往往藏在浏览器的某个静默行为里。

如果你打算自己写一个类似的脚本,我的建议是先把GM_cookie的官方文档通读一遍,然后在目标站点上手动登录一次,用开发者工具把真实 Cookie 的每一个属性都记下来,照着这个“标准答案”去写注入逻辑。不要凭想象填 Domain 和 Path,这两个字段是翻车重灾区。

最后分享一个调试小技巧:在脚本里加一个开关,通过 URL 参数控制是否输出详细日志。比如访问target-site.example/?debug=1时打印所有 Cookie 操作,平时则静默运行。这样既不影响日常使用,又能在出问题时快速定位。我用这个方法省下了大量“盲猜”的时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 13:03:13

基于ESP32-S3的工业级室内环境监测系统设计

简介:本资源是一份面向嵌入式开发初学者与物联网课程设计者的完整硬件系统设计方案,聚焦室内环境多参数智能监测场景。文档详细阐述了基于STM32主控与ESP8266 WiFi模块的软硬件协同实现路径,涵盖SHT20温湿度、BH1750光照、GP2Y1051AUOF PM2.5…

作者头像 李华
网站建设 2026/9/19 12:56:37

Hook是什么?一文看懂从Git到YOLOv8的钩子机制

你可能已经无数次在代码、报错日志或者某篇技术文章里见过 Hook 这个词,但每次查资料感觉都像在看黑话。今天我用最直接的方式把这件事讲清楚。Hook 翻译成“钩子”其实挺形象的:它就是程序运行过程中提前预留的挂钩点。你往这个点上挂一段自己的代码&am…

作者头像 李华