/** * 本地缓存查看代理(仅开发/排查用,不要部署到生产) * * 解决的问题:本地双击打开 work-view.html 时,浏览器是 file:// 跨域访问远程 * http://base.yunhaixingtong.com,出于安全策略【不会】把 CAS 登录 Cookie 带过去, * 导致每个请求都被 CAS 拦截返回 302。而浏览器又禁止 JS 用 fetch 设置 Cookie 请求头。 * * 做法:页面把“从 DevTools 复制的登录 Cookie”放进自定义头 x-auth-cookie 发给本代理, * 本代理在服务端把它注入成真实 Cookie 头,再转发到远程。远程因此认为是已登录的超管会话。 * * 用法: * 1) 确认本机装有 Node(>=14)。 * 2) 在本文件目录运行: node work/cache-proxy.js * 3) 浏览器打开 work-view.html,在“Cookie”框粘贴完整 Cookie 串(见下方说明)。 * 4) 页面 SERVER 默认 http://localhost:8899 即指向本代理。 * * 如何复制 Cookie:浏览器 DevTools → Application → Cookies → 选中 base.yunhaixingtong.com * 全选复制为 “name=value; name2=value2; ...” 形式(建议把该域下所有 Cookie 都带上, * 通常含 JSESSIONID / CASTGC 等,缺一不可)。 */ const http = require('http'); const https = require('https'); const { URL } = require('url'); const TARGET_BASE = 'http://192.168.10.166:8086'; const PORT = 8899; const server = http.createServer((req, res) => { // 允许本地 file:// 页面跨域调用本代理 res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type,x-auth-cookie'); if (req.method === 'OPTIONS') { res.writeHead(204); return res.end(); } // 登录态来源:优先页面/脚本发的 x-auth-cookie 头;也支持浏览器直接敲地址时带 ?cookie= 参数 const reqUrl = new URL(req.url, 'http://localhost'); const cookie = req.headers['x-auth-cookie'] || reqUrl.searchParams.get('cookie') || ''; if (!cookie) { res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' }); return res.end( JSON.stringify({ msg: '缺少登录态:页面请在“Cookie”框粘贴;或浏览器直接访问时附加 ?cookie=NAME=VALUE;NAME2=VALUE2', }) ); } // 从转发给远程的 URL 中去掉本地专用的 cookie 参数 reqUrl.searchParams.delete('cookie'); const target = new URL(TARGET_BASE + reqUrl.pathname + reqUrl.search); const isHttps = target.protocol === 'https:'; const options = { method: req.method, hostname: target.hostname, port: target.port || (isHttps ? 443 : 80), path: target.pathname + target.search, headers: { Cookie: cookie, Accept: 'application/json', 'Accept-Encoding': 'identity', 'User-Agent': req.headers['user-agent'] || 'cache-proxy', }, }; const transport = isHttps ? https : http; const proxyReq = transport.request(options, (proxyRes) => { // 远程未部署缓存接口:明确提示,避免把 Spring 404 白页甩给页面 if (proxyRes.statusCode === 404) { res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' }); res.end( JSON.stringify({ msg: '远程返回 404:该实例未部署缓存接口 /api/cache/*(请部署含 CacheController 的新 jar 到 ' + target.host + ')', }) ); return; } // 远程要求登录(CAS 拦截)或权限不足:回一封干净的 JSON 错误,避免把登录页 HTML 抛给页面 if ([301, 302, 401, 403].includes(proxyRes.statusCode)) { let buf = ''; proxyRes.on('data', (c) => (buf += c)); proxyRes.on('end', () => { res.writeHead(401, { 'Content-Type': 'application/json; charset=utf-8' }); res.end( JSON.stringify({ msg: '远程返回 ' + proxyRes.statusCode + ':Cookie 可能已失效或未登录,请重新从 DevTools 复制完整 Cookie', }) ); }); return; } res.writeHead(proxyRes.statusCode, { 'Content-Type': proxyRes.headers['content-type'] || 'application/json; charset=utf-8', }); proxyRes.pipe(res); }); proxyReq.on('error', (err) => { res.writeHead(502, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ msg: '代理转发失败:' + err.message })); }); req.pipe(proxyReq); }); server.listen(PORT, () => { console.log('[cache-proxy] 监听 http://localhost:' + PORT + ' -> ' + TARGET_BASE); });