多站点会话共享代理系统。支持动态添加任意网站,一台机器登录后,同网络下的其他机器无需登录即可复用认证状态。
┌──────────────────────────────┐
│ Proxy Server (:7890) │
│ │
│ sites.json sessions/ │
│ (站点注册表) ├ site-a.json │
│ ├ site-b.json │
│ └ ... │
│ │
│ Admin API SSE 广播 │
└──┬────────────┬──────────────┘
│ │
┌────────────────┼────────────┼───────────────
│ │ │ │
┌─────▼──────┐ ┌────▼─────┐ ┌──▼───────────┐ │
│ Electron A │ │Electron B│ │ Electron C │ │
│ (登录端) │ │ (免登录) │ │ (免登录) |
│ │ │ │ │ │ │
│ BrowserView│ │BrowserView│ │ BrowserView │ │
│ 直连目标站 │ │直连目标站 │ │ 直连目标站 │ │
└────────────┘ └──────────┘ └───────────────┘
系统由三部分组成:
| 模块 | 路径 | 职责 |
|---|---|---|
| Proxy Server | src/ |
中央认证数据存储 + 站点管理 + SSE 广播 + 反向代理 |
| Electron Client | electron-client/ |
内嵌浏览器,直连目标站,本地注入认证数据 |
Electron 客户端:BrowserView 直连目标网站,不经过 Proxy。认证数据从 Proxy 拉取后注入本地 session,请求由客户端自己发出。
Chrome 扩展:所有流量经 Proxy 反向代理转发,Proxy 的 onProxyReq 钩子在服务端注入 cookie/header。
Proxy 通过请求头 X-Proxy-Site: {siteId} 识别目标站点:
请求 → X-Proxy-Site 头 → SiteRegistry 查找 → 对应 SessionStore → 注入认证数据 → 转发
↓ (无头时)
默认站点回退
- Electron 客户端:BrowserView 的
onBeforeSendHeaders自动注入X-Proxy-Site头 - Chrome 扩展:不携带此头,使用默认站点回退机制
Client A 登录 → cookie 变更触发 → 自动推送到 Proxy
↓
Proxy 存储 + revision++
↓
SSE 广播 cookie-update (排除来源客户端)
↓
Client B/C 收到 → 注入本地 BrowserView session
防循环机制:
isSyncing标志 + 3 秒延迟释放:注入期间屏蔽本地 cookie changed 事件source跳过:SSE 消息携带 clientId,客户端跳过自身发起的变更revision去重:递增版本号,忽略 <= 本地版本的消息
npm install
npm startDocker 部署:
docker build -t shared-session-proxy .
docker run -d --name session-proxy --restart unless-stopped \
-p 7890:7890 \
-v $(pwd)/data:/app/data \
shared-session-proxy可选环境变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
PROXY_PORT |
7890 |
服务监听端口 |
PROXY_TARGET |
https://www.720yun.com |
初始默认站点(仅首次启动时生成) |
CLIENT_TOKEN |
空 | 代理请求访问令牌(空则不校验) |
USE_CLASH_PROXY |
false |
启用 Clash HTTPS 隧道(node start.js 启动时) |
健康检查:GET http://localhost:7890/__proxy_admin__/health
cd electron-client
npm install
npm start首次启动后在工具栏填入 Proxy 地址(如 http://192.168.7.44:7890),点击同步按钮。
- 首页九宫格 → 点击「添加站点」→ 输入名称和 URL
- 站点配置自动同步到 Proxy 和所有客户端
- 点击站点卡片打开 BrowserView → 正常登录
- 登录后 cookie 自动推送到 Proxy → SSE 广播 → 其他客户端注入认证数据
- 其他客户端打开同一站点 → 直接进入已登录状态
所有接口需携带 Authorization: Bearer 4110ba59-46e2-478a-b44f-e8e20413e842。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /sites |
列出所有站点 |
| GET | /sites/:siteId |
获取单个站点 |
| POST | /sites |
添加站点(body: {name, targetUrl, customHeaders?, startPage?}) |
| PUT | /sites/:siteId |
更新站点(merge 语义,siteId/addedAt 不可变) |
| DELETE | /sites/:siteId |
删除站点及其 session 数据 |
路径前缀均为 /__proxy_admin__。
站点数据模型:
{
"siteId": "github",
"name": "GitHub",
"targetUrl": "https://github.com",
"domains": [".github.com"],
"customHeaders": [],
"startPage": "/",
"addedAt": "2025-01-01T00:00:00.000Z"
}siteId:从 targetUrl hostname 自动生成(去 www、取首段、小写化),碰撞追加-2/-3domains:cookie domain 自动推导,也可手动指定customHeaders:需要捕获和注入的额外 header 名称(最多 10 个,禁止 host/cookie/authorization/x-proxy-site)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /session/status?siteId= |
站点 session 详情 |
| POST | /session/sync-cookies |
替换全量 cookies(body 含 siteId) |
| POST | /session/cookies |
合并更新 cookies |
| POST | /session/headers |
更新 headers |
| POST | /session/raw-cookie |
以原始字符串设置 cookie |
| DELETE | /session?siteId= |
清空站点 session |
siteId 缺失时回退到默认站点。
连接:GET /__proxy_admin__/session/events?token=<secret>&clientId=<id>
| 事件 | 说明 |
|---|---|
snapshot |
连接时推送:站点列表 + 各站点 session 摘要 |
cookie-update |
cookie 变更:含 siteId、cookies、revision、source |
site-added |
新站点添加 |
site-updated |
站点配置更新 |
site-removed |
站点删除 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
健康检查 + 各站点 session 摘要 |
| GET | /logs |
访问日志查询 |
| GET | /logs/stats |
日志统计 |
| POST | /logs/cleanup |
清理过期日志 |
shared-session-proxy/
├── src/
│ ├── server.js # 主服务:Express + 动态反向代理(http-proxy-middleware router 回调)
│ ├── config.js # 配置项(端口、目标站、令牌等)
│ ├── site-registry.js # 站点注册表:CRUD、siteId 生成、domain 推导、原子写入 sites.json
│ ├── session-store.js # Session 存储工厂:createSessionStore(siteId),按站点隔离
│ ├── admin-routes.js # 管理 API + SSE 端点 + 站点 CRUD
│ ├── access-log.js # SQLite 访问日志
│ └── middleware/
│ ├── auth.js # Bearer 认证(Admin API)+ 客户端令牌校验
│ └── logger.js # 请求日志
├── electron-client/
│ ├── main.js # 主进程:多 BrowserView 管理、per-site cookie 同步、SSE、站点管理 IPC
│ ├── preload.js # Context bridge:站点管理 + 导航 + 事件 API
│ ├── renderer/
│ │ └── index.html # 九宫格首页 + 工具栏 + 添加站点对话框 + 状态栏
│ └── package.json
├── chrome-extension/ # Chrome 扩展(反向代理模式,使用默认站点)
├── data/ # 运行时数据(git ignored)
│ ├── sites.json # 站点注册表
│ ├── sessions/ # 按站点隔离的 session 文件
│ │ ├── 720yun.json
│ │ ├── github.json
│ │ └── ...
│ └── proxy.db # 访问日志数据库
├── start.js # 启动入口(可选 Clash HTTPS 隧道)
├── Dockerfile
└── package.json
每个站点创建独立 BrowserView,session partition 为 persist:{siteId}-{clientId}。通过 Map<siteId, {view, viewSession, lastAccessed}> 管理,最多缓存 5 个(LRU 淘汰最旧)。
每个 BrowserView 的 session 注册 webRequest.onBeforeSendHeaders,执行:
- 注入
X-Proxy-Site: {siteId}头 - Cookie 合并:本地 cookie jar 已有的保留,从 Proxy 同步的共享 cookie 补充缺失项
- 共享 Header 注入:本地无值时从 Proxy 同步的 headers 填充
- customHeaders 捕获:站点配置中指定的 header 变化时推送到 Proxy
根据站点 targetUrl 自动推导 cookie domain(deriveDomains()):
https://www.example.com→[".example.com"]https://192.168.1.1→["192.168.1.1"]http://localhost→["localhost"]
cookie changed 事件按站点 domains 过滤,只处理匹配当前站点的 cookie。
路径:%APPDATA%/shared-session-browser/shared-proxy-config.json
{
"clientId": "自动生成的 UUID",
"userName": "User-xxxxxx",
"proxyUrl": "http://127.0.0.1:7890"
}日志:%APPDATA%/shared-session-browser/shared-proxy-client.log
docker build -t shared-session-proxy .
# 前台调试
docker run --rm -p 7890:7890 -v $(pwd)/data:/app/data shared-session-proxy
# 后台生产
docker run -d --name session-proxy --restart unless-stopped \
-p 7890:7890 -v $(pwd)/data:/app/data shared-session-proxycd electron-client
npm install
npm run dist输出便携版 exe 位于 electron-client/dist/。