114 lines
4.5 KiB
JavaScript
114 lines
4.5 KiB
JavaScript
/**
|
||
* 本地缓存查看代理(仅开发/排查用,不要部署到生产)
|
||
*
|
||
* 解决的问题:本地双击打开 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);
|
||
});
|