| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- /**
- * 验证「未登录访客由前端自己换 DMS token」这条链(用户 2026-09-29 要求)。
- *
- * 验证什么(都打**真实服务**,经过 dev server 的同源代理):
- * ① 前端模块 `getServiceDmsToken()` 能经 `/oauth/...` 用服务账号换到 JWT;
- * ② 拿这个 token **自己带 `token` 头**请求 `/dms/...` 能取到数据;
- * ③ ⚠️ 关键:**代理不覆盖**前端带的 token —— 故意带一个假 token,DMS 必须拒绝
- * (若代理仍无条件注入自己的 token,这里会错误地返回 200)。
- *
- * 前置:先起 dev server(脚本会自己起,端口 8101)。自签证书 → 用
- * `NODE_TLS_REJECT_UNAUTHORIZED=0` 跑(脚本内部已设置)。
- *
- * 怎么跑:
- * npx esbuild harness/tools/_entry-dms-service-token.ts --bundle --format=esm \
- * --outfile=harness/tools/_dms-service-token.mjs --alias:@=./src --define:import.meta.env='{}'
- * node harness/tools/verify-dms-service-token.mjs
- */
- import { spawn } from 'node:child_process';
- import { setTimeout as sleep } from 'node:timers/promises';
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
- const PORT = 8101;
- const ORIGIN = `https://localhost:${PORT}`;
- let pass = 0;
- let fail = 0;
- const check = (label, ok, extra = '') => {
- if (ok) {
- pass += 1;
- console.log(` ok ${label}${extra ? ` —— ${extra}` : ''}`);
- } else {
- fail += 1;
- console.log(` FAIL ${label}${extra ? ` —— ${extra}` : ''}`);
- }
- };
- const dev = spawn('npx', ['vite', '--port', String(PORT), '--mode', 'development'], {
- cwd: process.cwd(),
- shell: true,
- stdio: 'ignore',
- });
- const stopDev = () => {
- try {
- if (process.platform === 'win32') spawn('taskkill', ['/PID', String(dev.pid), '/T', '/F'], { shell: true });
- else dev.kill('SIGTERM');
- } catch {
- /* 尽力而为 */
- }
- };
- try {
- // 等 dev server 起来
- for (let i = 0; i < 40; i += 1) {
- await sleep(1000);
- try {
- const res = await fetch(`${ORIGIN}/`);
- if (res.ok) break;
- } catch {
- /* 还没起来 */
- }
- }
- // 模块在 node 里跑:相对路径没法直接 fetch,所以用 globalThis 覆盖成绝对地址
- globalThis.VITE_OAUTH_LOGIN_URL = `${ORIGIN}/oauth/api/user/login`;
- const { getServiceDmsToken, clearServiceDmsToken } = await import('./_dms-service-token.mjs');
- console.log('\n【1】前端模块换 token(经 /oauth 同源代理)');
- let token = '';
- try {
- token = await getServiceDmsToken();
- } catch (error) {
- check('换 token 成功', false, error instanceof Error ? error.message : String(error));
- }
- check('换 token 成功且是 JWT 形状', /^ey[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\./.test(token), `长度 ${token.length}`);
- check('再次调用命中缓存(同一 token)', (await getServiceDmsToken()) === token);
- console.log('\n【2】带着这个 token 走 /dms 代理读写');
- const dmsCall = async (tokenHeader) => {
- const res = await fetch(`${ORIGIN}/dms/content/selectContentList`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...(tokenHeader ? { token: tokenHeader } : {}) },
- body: new URLSearchParams({ columnId: '1889', page: '1', size: '1' }).toString(),
- });
- return res.json();
- };
- const okBody = await dmsCall(token);
- check('带真 token → code 200 且有数据', okBody?.code === 200 && (okBody?.content?.data?.length ?? 0) > 0, `code=${okBody?.code}`);
- console.log('\n【3】代理不覆盖前端带的 token(关键断言)');
- const bogus = await dmsCall('this-is-not-a-valid-token');
- check(
- '带假 token → DMS 拒绝(201/212),说明代理没有偷偷注入自己的 token',
- bogus?.code === 201 || bogus?.code === 212,
- `code=${bogus?.code} message=${String(bogus?.message ?? '').slice(0, 40)}`
- );
- console.log('\n【4】不带 token → 仍由代理兜底注入(命令行走代理的排查方式)');
- const viaProxy = await dmsCall('');
- check('不带 token → 代理注入 `.dms-token` 后 200', viaProxy?.code === 200, `code=${viaProxy?.code}`);
- clearServiceDmsToken();
- } finally {
- stopDev();
- }
- console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
- process.exit(fail ? 1 : 0);
|