From 1c1f370d5923edb6fa3515a73eef16c98be013f0 Mon Sep 17 00:00:00 2001 From: dongzp <975303544@qq.com> Date: Wed, 22 Jul 2026 10:01:23 +0800 Subject: [PATCH] fix(ws): proxy Baidu WebSocket via Vite to bypass Origin 403 Co-authored-by: Cursor --- .../specs/2026-07-22-baidu-quote-ws-design.md | 6 ++--- src/api/baidu/ws.ts | 13 +++++++++-- vite.config.ts | 22 ++++++++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/specs/2026-07-22-baidu-quote-ws-design.md b/docs/superpowers/specs/2026-07-22-baidu-quote-ws-design.md index 87612b5..52dc943 100644 --- a/docs/superpowers/specs/2026-07-22-baidu-quote-ws-design.md +++ b/docs/superpowers/specs/2026-07-22-baidu-quote-ws-design.md @@ -31,7 +31,7 @@ ``` 页面挂载 → quota.fetchQuote() // HTTP 全量 - → BaiduQuoteWs.connect() // wss://finance-ws.pae.baidu.com/ + → BaiduQuoteWs.connect() // ws(s)://{host}/finance-ws/ → Vite/Nginx 代理到百度 → subscribe tick + snapshot → 每 6s ping → 每 60s patch snapshot @@ -58,7 +58,7 @@ ## 4. 协议细节 -**端点:** `wss://finance-ws.pae.baidu.com/` +**端点:** 浏览器连同域 `/finance-ws/`;由 Vite(开发)或 Nginx(生产)代理到 `wss://finance-ws.pae.baidu.com/`,并改写 `Origin`/`Referer` 为 `https://gushitong.baidu.com`(否则 localhost Origin 会 403)。 **订阅(open 后立即发送两条):** @@ -143,4 +143,4 @@ - WS 与 HTTP 的 snapshot 字段结构不完全一致(HTTP 用 `origin_pankou`,WS 用 `pankouinfos` 数组);mapper 需独立,勿强行复用 HTTP mapper 全文 - 去重键不含服务端唯一 id(协议未提供);极端情况下同秒同价同量同向两笔可能被误去重,可接受 -- 跨域 / 鉴权:浏览器直连百度 WS;若环境拦截再评估代理,本期先直连 +- 跨域 / 鉴权:浏览器不能直连(Origin=localhost → 403)。开发经 Vite 代理 `/finance-ws`,并把 Origin/Referer 改写为 `https://gushitong.baidu.com`;生产需同等反向代理。 diff --git a/src/api/baidu/ws.ts b/src/api/baidu/ws.ts index dec11c5..45751e8 100644 --- a/src/api/baidu/ws.ts +++ b/src/api/baidu/ws.ts @@ -1,12 +1,21 @@ import { contractConfig } from '../../config/contract' import type { BaiduWsItem, BaiduWsMessage, BaiduWsOutbound } from './wsTypes' -const WS_URL = 'wss://finance-ws.pae.baidu.com/' const PING_MS = 6_000 const PATCH_MS = 60_000 const RECONNECT_BASE_MS = 1_000 const RECONNECT_MAX_MS = 30_000 +/** + * Same-origin path proxied by Vite/Nginx to wss://finance-ws.pae.baidu.com. + * Direct browser connect sends Origin=localhost and gets 403. + */ +function resolveWsUrl(): string { + const proto = typeof location !== 'undefined' && location.protocol === 'https:' ? 'wss:' : 'ws:' + const host = typeof location !== 'undefined' ? location.host : 'localhost:5173' + return `${proto}//${host}/finance-ws/` +} + export type BaiduQuoteWsHandlers = { onMessage: (msg: BaiduWsMessage) => void onError?: (err: unknown) => void @@ -71,7 +80,7 @@ export class BaiduQuoteWs { private openSocket(): void { if (this.intentionalClose) return - const ws = new WebSocket(WS_URL) + const ws = new WebSocket(resolveWsUrl()) this.ws = ws ws.onopen = () => { diff --git a/vite.config.ts b/vite.config.ts index 9f38d3a..86d5a25 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,8 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' +const BAIDU_ORIGIN = 'https://gushitong.baidu.com' + export default defineConfig({ plugins: [vue()], resolve: { @@ -11,12 +13,30 @@ export default defineConfig({ }, server: { proxy: { + /** + * WebSocket must be registered before `/baidu` so it is not swallowed. + * Baidu rejects non-baidu Origins (localhost → 403); rewrite Origin here. + */ + '/finance-ws': { + target: 'wss://finance-ws.pae.baidu.com', + changeOrigin: true, + ws: true, + rewrite: (path) => path.replace(/^\/finance-ws/, '') || '/', + configure: (proxy) => { + const setBaiduHeaders = (proxyReq: { setHeader: (k: string, v: string) => void }) => { + proxyReq.setHeader('Origin', BAIDU_ORIGIN) + proxyReq.setHeader('Referer', `${BAIDU_ORIGIN}/`) + } + proxy.on('proxyReq', setBaiduHeaders) + proxy.on('proxyReqWs', setBaiduHeaders) + }, + }, '/baidu': { target: 'https://finance.pae.baidu.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/baidu/, ''), headers: { - Referer: 'https://gushitong.baidu.com/', + Referer: `${BAIDU_ORIGIN}/`, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', },