Files
2026-09-02 10:23:24 +08:00

73 lines
2.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// /oapi 签名票据生成器(对齐 yh-platform-net SignUtil.SignRequest / SignCanonical 协议)
//
// 协议:signature = Base64(HmacSHA256(METHOD + "\n" + path[?sortedQuery] + "\n" + timestamp, secret))
// - query 按 k=v 整体字典序排序后拼接参与签名
// - timestamp 为毫秒级 Unix 时间戳(服务端校验 5 分钟窗口)
//
// 用法:
// node scripts/oapi-sign.js [METHOD] [path?query]
// node scripts/oapi-sign.js --curl POST /oapi/print/submit # 输出可直接复制的 curl 命令
//
// 密钥来源(优先级):环境变量 APP_KEY / APP_SECRET → 文件默认值(开发联调密钥)
// 生产环境务必通过环境变量注入,勿提交真实密钥。
const crypto = require('crypto');
const APP_KEY = process.env.APP_KEY || '100002';
const APP_SECRET = process.env.APP_SECRET || '2314a3b158b24a838b2b9b69fd3db118';
// 排序 query(对齐 SignCanonical.SortRawQuery:k=v 整体排序,空值保留 = 号)
function sortQuery(raw) {
if (!raw) return '';
return raw.split('&').filter(Boolean).map(p => {
const i = p.indexOf('=');
return (i < 0 ? p : p.slice(0, i)) + '=' + (i < 0 ? '' : p.slice(i + 1));
}).sort().join('&');
}
function sign(method, pathWithQuery, ts) {
const qi = pathWithQuery.indexOf('?');
const target = qi >= 0
? pathWithQuery.slice(0, qi) + '?' + sortQuery(pathWithQuery.slice(qi + 1))
: pathWithQuery;
const source = method.toUpperCase() + '\n' + target + '\n' + ts;
const sig = crypto.createHmac('sha256', APP_SECRET).update(source, 'utf8').digest('base64');
return { ts, target, signature: sig };
}
const args = process.argv.slice(2);
let method = 'GET';
let pathWithQuery = '/oapi/hello';
let curlMode = false;
for (let i = 0; i < args.length; i++) {
const a = args[i];
if (a === '--curl') { curlMode = true; continue; }
if (!/^-/.test(a)) {
if (method === 'GET' && !/^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/i.test(a) && !a.startsWith('/')) {
// 第一个非选项参数若不像 METHOD 也不像路径,则视为路径
pathWithQuery = a;
} else if (a.startsWith('/')) {
pathWithQuery = a;
} else {
method = a;
}
}
}
const { ts, target, signature } = sign(method, pathWithQuery, Date.now().toString());
if (curlMode) {
const u = target.replace(/^\//, '');
console.log(`curl -i -X ${method.toUpperCase()} "http://localhost:PORT/${u}" \\`);
console.log(` -H "X-App-Key: ${APP_KEY}" \\`);
console.log(` -H "X-Timestamp: ${ts}" \\`);
console.log(` -H "X-Signature: ${signature}"`);
} else {
console.log(JSON.stringify({ appKey: APP_KEY, method, target, ts, signature, curl: [
'-H', `X-App-Key: ${APP_KEY}`,
'-H', `X-Timestamp: ${ts}`,
'-H', `X-Signature: ${signature}`
].join(' ') }, null, 2));
}