@antares-core/rvs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/rvs.decorators.d.ts +18 -0
- package/dist/rvs.decorators.js +28 -0
- package/dist/rvs.decorators.js.map +1 -0
- package/dist/rvs.middleware.d.ts +30 -0
- package/dist/rvs.middleware.js +145 -0
- package/dist/rvs.middleware.js.map +1 -0
- package/dist/rvs.module.d.ts +19 -0
- package/dist/rvs.module.js +119 -0
- package/dist/rvs.module.js.map +1 -0
- package/dist/rvs.registry.d.ts +31 -0
- package/dist/rvs.registry.js +100 -0
- package/dist/rvs.registry.js.map +1 -0
- package/dist/rvs.service.d.ts +21 -0
- package/dist/rvs.service.js +49 -0
- package/dist/rvs.service.js.map +1 -0
- package/dist/rvs.tokens.d.ts +2 -0
- package/dist/rvs.tokens.js +6 -0
- package/dist/rvs.tokens.js.map +1 -0
- package/dist/rvs.types.d.ts +60 -0
- package/dist/rvs.types.js +20 -0
- package/dist/rvs.types.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 antares-core
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./rvs.types"), exports);
|
|
18
|
+
__exportStar(require("./rvs.tokens"), exports);
|
|
19
|
+
__exportStar(require("./rvs.decorators"), exports);
|
|
20
|
+
__exportStar(require("./rvs.registry"), exports);
|
|
21
|
+
__exportStar(require("./rvs.service"), exports);
|
|
22
|
+
__exportStar(require("./rvs.middleware"), exports);
|
|
23
|
+
__exportStar(require("./rvs.module"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,+CAA6B;AAC7B,mDAAiC;AACjC,iDAA+B;AAC/B,gDAA8B;AAC9B,mDAAiC;AACjC,+CAA6B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const RVS_SKIP_METADATA = "antares:rvs:skip";
|
|
2
|
+
export declare const RVS_FORCE_METADATA = "antares:rvs:force";
|
|
3
|
+
/**
|
|
4
|
+
* 跳过该控制器/接口的 RVS 验签与响应加密。
|
|
5
|
+
* 适用:健康检查、回调通知、公开静态资源。
|
|
6
|
+
* 优先级低于 @RvsForce;命名与内核 crypto-kit 的 @CryptoSkip 对齐。
|
|
7
|
+
*/
|
|
8
|
+
export declare const RvsSkip: () => ClassDecorator & MethodDecorator;
|
|
9
|
+
/**
|
|
10
|
+
* 强制验签:即使命中 skipPaths 或类级 @RvsSkip 也执行校验。
|
|
11
|
+
* 适用:高价值接口在全局放行策略下单独收紧。
|
|
12
|
+
*/
|
|
13
|
+
export declare const RvsForce: () => ClassDecorator & MethodDecorator;
|
|
14
|
+
/**
|
|
15
|
+
* 取当前请求的验签上下文(req.rvs)。
|
|
16
|
+
* 上下文含请求级密钥材料,只允许在业务方法内使用,禁止日志输出或透传响应。
|
|
17
|
+
*/
|
|
18
|
+
export declare const RvsCtx: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RvsCtx = exports.RvsForce = exports.RvsSkip = exports.RVS_FORCE_METADATA = exports.RVS_SKIP_METADATA = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
exports.RVS_SKIP_METADATA = 'antares:rvs:skip';
|
|
6
|
+
exports.RVS_FORCE_METADATA = 'antares:rvs:force';
|
|
7
|
+
/**
|
|
8
|
+
* 跳过该控制器/接口的 RVS 验签与响应加密。
|
|
9
|
+
* 适用:健康检查、回调通知、公开静态资源。
|
|
10
|
+
* 优先级低于 @RvsForce;命名与内核 crypto-kit 的 @CryptoSkip 对齐。
|
|
11
|
+
*/
|
|
12
|
+
const RvsSkip = () => (0, common_1.SetMetadata)(exports.RVS_SKIP_METADATA, true);
|
|
13
|
+
exports.RvsSkip = RvsSkip;
|
|
14
|
+
/**
|
|
15
|
+
* 强制验签:即使命中 skipPaths 或类级 @RvsSkip 也执行校验。
|
|
16
|
+
* 适用:高价值接口在全局放行策略下单独收紧。
|
|
17
|
+
*/
|
|
18
|
+
const RvsForce = () => (0, common_1.SetMetadata)(exports.RVS_FORCE_METADATA, true);
|
|
19
|
+
exports.RvsForce = RvsForce;
|
|
20
|
+
/**
|
|
21
|
+
* 取当前请求的验签上下文(req.rvs)。
|
|
22
|
+
* 上下文含请求级密钥材料,只允许在业务方法内使用,禁止日志输出或透传响应。
|
|
23
|
+
*/
|
|
24
|
+
exports.RvsCtx = (0, common_1.createParamDecorator)((property, ctx) => {
|
|
25
|
+
const request = ctx.switchToHttp().getRequest();
|
|
26
|
+
return request?.[property || 'rvs'];
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=rvs.decorators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.decorators.js","sourceRoot":"","sources":["../src/rvs.decorators.ts"],"names":[],"mappings":";;;AAAA,2CAA0F;AAE7E,QAAA,iBAAiB,GAAG,kBAAkB,CAAC;AACvC,QAAA,kBAAkB,GAAG,mBAAmB,CAAC;AAEtD;;;;GAIG;AACI,MAAM,OAAO,GAAG,GAAqC,EAAE,CAAC,IAAA,oBAAW,EAAC,yBAAiB,EAAE,IAAI,CAAC,CAAC;AAAvF,QAAA,OAAO,WAAgF;AAEpG;;;GAGG;AACI,MAAM,QAAQ,GAAG,GAAqC,EAAE,CAAC,IAAA,oBAAW,EAAC,0BAAkB,EAAE,IAAI,CAAC,CAAC;AAAzF,QAAA,QAAQ,YAAiF;AAEtG;;;GAGG;AACU,QAAA,MAAM,GAAG,IAAA,6BAAoB,EAAC,CAAC,QAA4B,EAAE,GAAqB,EAAE,EAAE;IACjG,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;IAChD,OAAO,OAAO,EAAE,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type NestMiddleware } from '@nestjs/common';
|
|
2
|
+
import { type RvsServer } from 'rand-vec-sig';
|
|
3
|
+
import { RvsRouteRegistry } from './rvs.registry';
|
|
4
|
+
import { type ResolvedRvsOptions } from './rvs.types';
|
|
5
|
+
/**
|
|
6
|
+
* RVS 全局中间件(Express 壳层)。
|
|
7
|
+
*
|
|
8
|
+
* 职责顺序(顺序即安全语义,改动前请先读注释):
|
|
9
|
+
* 1. 动态会话端点直通:客户端拿 sessSeed 的引导请求不能要求先验签
|
|
10
|
+
* 2. @RvsForce 命中 → 无条件验签(覆盖 skipPaths / @RvsSkip)
|
|
11
|
+
* 3. skipPaths / @RvsSkip 命中 → 直通
|
|
12
|
+
* 4. tolerant 模式且未携带标识头 → 视为明文请求放行(已带标识头但验签失败仍拒绝)
|
|
13
|
+
* 5. 安装响应加密出口(包住 res.json,保证拦截器包装后的最终报文被整体加密)
|
|
14
|
+
* 6. 委托 rand-vec-sig 中间件完成验签/解密/上下文挂载/防重放
|
|
15
|
+
*
|
|
16
|
+
* 响应加密必须在 res.json 层完成:客户端按顶层混淆信封(alg="r")解密,
|
|
17
|
+
* 因此任何 Nest 拦截器(统一响应壳 / 序列化)产生的最终 body 才是加密输入。
|
|
18
|
+
*/
|
|
19
|
+
export declare class RvsMiddleware implements NestMiddleware {
|
|
20
|
+
private readonly server;
|
|
21
|
+
private readonly options;
|
|
22
|
+
private readonly registry;
|
|
23
|
+
private readonly expressMiddleware;
|
|
24
|
+
private readonly identifyHeaderNames;
|
|
25
|
+
constructor(server: RvsServer, options: ResolvedRvsOptions, registry: RvsRouteRegistry);
|
|
26
|
+
use(req: Record<string, any>, res: Record<string, any>, next: (err?: unknown) => void): void;
|
|
27
|
+
private isSessionPath;
|
|
28
|
+
private hasIdentifyHeaders;
|
|
29
|
+
private patchResponseJson;
|
|
30
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RvsMiddleware = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const core_1 = require("@antares-core/core");
|
|
18
|
+
const rand_vec_sig_1 = require("rand-vec-sig");
|
|
19
|
+
const rvs_registry_1 = require("./rvs.registry");
|
|
20
|
+
const rvs_tokens_1 = require("./rvs.tokens");
|
|
21
|
+
const rvs_types_1 = require("./rvs.types");
|
|
22
|
+
/**
|
|
23
|
+
* RVS 全局中间件(Express 壳层)。
|
|
24
|
+
*
|
|
25
|
+
* 职责顺序(顺序即安全语义,改动前请先读注释):
|
|
26
|
+
* 1. 动态会话端点直通:客户端拿 sessSeed 的引导请求不能要求先验签
|
|
27
|
+
* 2. @RvsForce 命中 → 无条件验签(覆盖 skipPaths / @RvsSkip)
|
|
28
|
+
* 3. skipPaths / @RvsSkip 命中 → 直通
|
|
29
|
+
* 4. tolerant 模式且未携带标识头 → 视为明文请求放行(已带标识头但验签失败仍拒绝)
|
|
30
|
+
* 5. 安装响应加密出口(包住 res.json,保证拦截器包装后的最终报文被整体加密)
|
|
31
|
+
* 6. 委托 rand-vec-sig 中间件完成验签/解密/上下文挂载/防重放
|
|
32
|
+
*
|
|
33
|
+
* 响应加密必须在 res.json 层完成:客户端按顶层混淆信封(alg="r")解密,
|
|
34
|
+
* 因此任何 Nest 拦截器(统一响应壳 / 序列化)产生的最终 body 才是加密输入。
|
|
35
|
+
*/
|
|
36
|
+
let RvsMiddleware = class RvsMiddleware {
|
|
37
|
+
constructor(server, options, registry) {
|
|
38
|
+
this.server = server;
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.registry = registry;
|
|
41
|
+
this.expressMiddleware = server.middleware({
|
|
42
|
+
// 跳过判定在 use() 中统一完成(需要叠加装饰器注册表),此处不再传 skipPaths
|
|
43
|
+
skipPaths: [],
|
|
44
|
+
contextProperty: options.contextProperty,
|
|
45
|
+
replaceBody: true,
|
|
46
|
+
onReject: (result, request) => {
|
|
47
|
+
core_1.logger.warn('rvs request rejected', {
|
|
48
|
+
code: result.code,
|
|
49
|
+
status: result.status,
|
|
50
|
+
url: request?.url,
|
|
51
|
+
method: request?.method,
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const overrides = options.identifyHeaders ?? {};
|
|
56
|
+
this.identifyHeaderNames = [
|
|
57
|
+
(overrides.version ?? rand_vec_sig_1.DEFAULT_IDENTIFY_HEADERS.version).toLowerCase(),
|
|
58
|
+
(overrides.session ?? rand_vec_sig_1.DEFAULT_IDENTIFY_HEADERS.session).toLowerCase(),
|
|
59
|
+
(overrides.nonce ?? rand_vec_sig_1.DEFAULT_IDENTIFY_HEADERS.nonce).toLowerCase(),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
use(req, res, next) {
|
|
63
|
+
const url = req.originalUrl ?? req.url ?? '';
|
|
64
|
+
const pathname = url.split('?')[0];
|
|
65
|
+
// 1) 动态会话端点
|
|
66
|
+
if (this.options.sessionEndpoint && req.method === 'GET' && this.isSessionPath(pathname)) {
|
|
67
|
+
try {
|
|
68
|
+
res.json(this.server.issueSession());
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
next(err);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// 2) @RvsForce 优先于一切放行规则
|
|
76
|
+
const forced = this.registry.isForced(url);
|
|
77
|
+
if (!forced) {
|
|
78
|
+
// 3) 放行清单
|
|
79
|
+
if (this.registry.isSkipped(url) || (0, rvs_types_1.matchesAnyPath)(this.options.skipPaths, url)) {
|
|
80
|
+
next();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// 4) 明文请求(tolerant 模式)
|
|
84
|
+
if (this.options.unprotected === 'tolerant' && !this.hasIdentifyHeaders(req)) {
|
|
85
|
+
next();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// 5) 响应加密出口
|
|
90
|
+
if (this.options.responseEncryption)
|
|
91
|
+
this.patchResponseJson(req, res);
|
|
92
|
+
// 6) 验签/解密;rand-vec-sig 中间件内部已捕获异常并转 next(error)
|
|
93
|
+
void this.expressMiddleware(req, res, next);
|
|
94
|
+
}
|
|
95
|
+
isSessionPath(pathname) {
|
|
96
|
+
const endpoint = this.options.sessionEndpoint;
|
|
97
|
+
return pathname === endpoint || pathname.endsWith(endpoint);
|
|
98
|
+
}
|
|
99
|
+
hasIdentifyHeaders(req) {
|
|
100
|
+
const headers = req.headers;
|
|
101
|
+
if (!headers)
|
|
102
|
+
return false;
|
|
103
|
+
return this.identifyHeaderNames.some((name) => {
|
|
104
|
+
if (typeof headers.get === 'function') {
|
|
105
|
+
return Boolean(headers.get(name));
|
|
106
|
+
}
|
|
107
|
+
return Boolean(headers[name]);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
patchResponseJson(req, res) {
|
|
111
|
+
if (typeof res.json !== 'function')
|
|
112
|
+
return;
|
|
113
|
+
const original = res.json.bind(res);
|
|
114
|
+
const property = this.options.contextProperty;
|
|
115
|
+
res.json = (body) => {
|
|
116
|
+
const context = req[property];
|
|
117
|
+
if (!context)
|
|
118
|
+
return original(body);
|
|
119
|
+
try {
|
|
120
|
+
return original(this.server.encryptResponse(body, context));
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
// 加密失败绝不回退明文(会泄露业务数据);改为通用错误壳,由客户端按明文错误处理
|
|
124
|
+
core_1.alertBus.error('rvs:response-encrypt-failed', 'RVS 响应加密失败,已阻止明文业务数据返回', {
|
|
125
|
+
reason: err?.message,
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
res.statusCode = 500;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
/* 头已发出则忽略 */
|
|
132
|
+
}
|
|
133
|
+
return original({ code: 'RVS_RESPONSE_ENCRYPT_FAILED', message: 'response encryption failed' });
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
exports.RvsMiddleware = RvsMiddleware;
|
|
139
|
+
exports.RvsMiddleware = RvsMiddleware = __decorate([
|
|
140
|
+
(0, common_1.Injectable)(),
|
|
141
|
+
__param(0, (0, common_1.Inject)(rvs_tokens_1.RVS_SERVER)),
|
|
142
|
+
__param(1, (0, common_1.Inject)(rvs_tokens_1.RVS_OPTIONS)),
|
|
143
|
+
__metadata("design:paramtypes", [Object, Object, rvs_registry_1.RvsRouteRegistry])
|
|
144
|
+
], RvsMiddleware);
|
|
145
|
+
//# sourceMappingURL=rvs.middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.middleware.js","sourceRoot":"","sources":["../src/rvs.middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAyE;AACzE,6CAAsD;AACtD,+CAAsH;AACtH,iDAAkD;AAClD,6CAAuD;AACvD,2CAAsE;AAEtE;;;;;;;;;;;;;GAaG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAa;IAIxB,YACuC,MAAiB,EAChB,OAA2B,EAChD,QAA0B;QAFN,WAAM,GAAN,MAAM,CAAW;QAChB,YAAO,GAAP,OAAO,CAAoB;QAChD,aAAQ,GAAR,QAAQ,CAAkB;QAE3C,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC;YACzC,gDAAgD;YAChD,SAAS,EAAE,EAAE;YACb,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;gBAC5B,aAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE;oBAClC,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,GAAG,EAAE,OAAO,EAAE,GAAG;oBACjB,MAAM,EAAE,OAAO,EAAE,MAAM;iBACxB,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,mBAAmB,GAAG;YACzB,CAAC,SAAS,CAAC,OAAO,IAAI,uCAAwB,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;YACrE,CAAC,SAAS,CAAC,OAAO,IAAI,uCAAwB,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;YACrE,CAAC,SAAS,CAAC,KAAK,IAAI,uCAAwB,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE;SAClE,CAAC;IACJ,CAAC;IAED,GAAG,CAAC,GAAwB,EAAE,GAAwB,EAAE,IAA6B;QACnF,MAAM,GAAG,GAAW,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnC,YAAY;QACZ,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzF,IAAI,CAAC;gBACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,CAAC;YACZ,CAAC;YACD,OAAO;QACT,CAAC;QAED,yBAAyB;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,UAAU;YACV,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAA,0BAAc,EAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChF,IAAI,EAAE,CAAC;gBACP,OAAO;YACT,CAAC;YACD,uBAAuB;YACvB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7E,IAAI,EAAE,CAAC;gBACP,OAAO;YACT,CAAC;QACH,CAAC;QAED,YAAY;QACZ,IAAI,IAAI,CAAC,OAAO,CAAC,kBAAkB;YAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAEtE,iDAAiD;QACjD,KAAK,IAAI,CAAC,iBAAiB,CAAC,GAAY,EAAE,GAAY,EAAE,IAAI,CAAC,CAAC;IAChE,CAAC;IAEO,aAAa,CAAC,QAAgB;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAyB,CAAC;QACxD,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9D,CAAC;IAEO,kBAAkB,CAAC,GAAwB;QACjD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAqF,CAAC;QAC1G,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,OAAO,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAC5C,IAAI,OAAQ,OAA6B,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC7D,OAAO,OAAO,CAAE,OAAgD,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO,OAAO,CAAE,OAAmC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,iBAAiB,CAAC,GAAwB,EAAE,GAAwB;QAC1E,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO;QAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;QAC9C,GAAG,CAAC,IAAI,GAAG,CAAC,IAAa,EAAE,EAAE;YAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAuC,CAAC;YACpE,IAAI,CAAC,OAAO;gBAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,0CAA0C;gBAC1C,eAAQ,CAAC,KAAK,CAAC,6BAA6B,EAAE,wBAAwB,EAAE;oBACtE,MAAM,EAAG,GAAa,EAAE,OAAO;iBAChC,CAAC,CAAC;gBACH,IAAI,CAAC;oBACH,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACvB,CAAC;gBAAC,MAAM,CAAC;oBACP,aAAa;gBACf,CAAC;gBACD,OAAO,QAAQ,CAAC,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC;YAClG,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;CACF,CAAA;AA1GY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;IAMR,WAAA,IAAA,eAAM,EAAC,uBAAU,CAAC,CAAA;IAClB,WAAA,IAAA,eAAM,EAAC,wBAAW,CAAC,CAAA;qDACO,+BAAgB;GAPlC,aAAa,CA0GzB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type DynamicModule, type MiddlewareConsumer, type NestModule } from '@nestjs/common';
|
|
2
|
+
import { type ResolvedRvsOptions, type RvsModuleOptions } from './rvs.types';
|
|
3
|
+
/**
|
|
4
|
+
* RVS 适配模块:全局验签/解密 + 响应加密 + 装饰器粒度控制 + 会话端点。
|
|
5
|
+
*
|
|
6
|
+
* imports: [RvsModule.forRoot({ mode: 'static', serverPrivateKey: env.RVS_RSK, redis: redisClient })]
|
|
7
|
+
*
|
|
8
|
+
* 装配注意:
|
|
9
|
+
* - 与 crypto-kit 互斥:启用 RVS 时宿主应关闭 cryptoOption.enableCrypto(双加密通道无法共存)
|
|
10
|
+
* - 多实例必须传 redis(防重放存储共享),否则启动期告警
|
|
11
|
+
* - 响应加密依赖 res.json 出口,@Res() 直写响应不经过加密(与内核 Transform 拦截器的既有约定一致)
|
|
12
|
+
*/
|
|
13
|
+
export declare class RvsModule implements NestModule {
|
|
14
|
+
private readonly options;
|
|
15
|
+
constructor(options: ResolvedRvsOptions);
|
|
16
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
17
|
+
static forRoot(options: RvsModuleOptions): DynamicModule;
|
|
18
|
+
static forRootAsync(factory: () => RvsModuleOptions | Promise<RvsModuleOptions>): Promise<DynamicModule>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RvsModule = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const core_1 = require("@nestjs/core");
|
|
18
|
+
const core_2 = require("@antares-core/core");
|
|
19
|
+
const rand_vec_sig_1 = require("rand-vec-sig");
|
|
20
|
+
const rvs_middleware_1 = require("./rvs.middleware");
|
|
21
|
+
const rvs_registry_1 = require("./rvs.registry");
|
|
22
|
+
const rvs_service_1 = require("./rvs.service");
|
|
23
|
+
const rvs_tokens_1 = require("./rvs.tokens");
|
|
24
|
+
const rvs_types_1 = require("./rvs.types");
|
|
25
|
+
/**
|
|
26
|
+
* RVS 适配模块:全局验签/解密 + 响应加密 + 装饰器粒度控制 + 会话端点。
|
|
27
|
+
*
|
|
28
|
+
* imports: [RvsModule.forRoot({ mode: 'static', serverPrivateKey: env.RVS_RSK, redis: redisClient })]
|
|
29
|
+
*
|
|
30
|
+
* 装配注意:
|
|
31
|
+
* - 与 crypto-kit 互斥:启用 RVS 时宿主应关闭 cryptoOption.enableCrypto(双加密通道无法共存)
|
|
32
|
+
* - 多实例必须传 redis(防重放存储共享),否则启动期告警
|
|
33
|
+
* - 响应加密依赖 res.json 出口,@Res() 直写响应不经过加密(与内核 Transform 拦截器的既有约定一致)
|
|
34
|
+
*/
|
|
35
|
+
let RvsModule = class RvsModule {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.options = options;
|
|
38
|
+
}
|
|
39
|
+
configure(consumer) {
|
|
40
|
+
consumer.apply(rvs_middleware_1.RvsMiddleware).forRoutes('*');
|
|
41
|
+
}
|
|
42
|
+
static forRoot(options) {
|
|
43
|
+
return buildRvsModule((0, rvs_types_1.resolveRvsOptions)(options));
|
|
44
|
+
}
|
|
45
|
+
static async forRootAsync(factory) {
|
|
46
|
+
return buildRvsModule((0, rvs_types_1.resolveRvsOptions)(await factory()));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.RvsModule = RvsModule;
|
|
50
|
+
exports.RvsModule = RvsModule = __decorate([
|
|
51
|
+
(0, common_1.Module)({}),
|
|
52
|
+
__param(0, (0, common_1.Inject)(rvs_tokens_1.RVS_OPTIONS)),
|
|
53
|
+
__metadata("design:paramtypes", [Object])
|
|
54
|
+
], RvsModule);
|
|
55
|
+
function buildRvsModule(options) {
|
|
56
|
+
return {
|
|
57
|
+
module: RvsModule,
|
|
58
|
+
imports: [core_1.DiscoveryModule],
|
|
59
|
+
providers: [
|
|
60
|
+
{ provide: rvs_tokens_1.RVS_OPTIONS, useValue: options },
|
|
61
|
+
{ provide: rvs_tokens_1.RVS_SERVER, useFactory: () => createRvsServer(options) },
|
|
62
|
+
rvs_registry_1.RvsRouteRegistry,
|
|
63
|
+
rvs_service_1.RvsService,
|
|
64
|
+
rvs_middleware_1.RvsMiddleware,
|
|
65
|
+
],
|
|
66
|
+
exports: [rvs_tokens_1.RVS_SERVER, rvs_tokens_1.RVS_OPTIONS, rvs_service_1.RvsService],
|
|
67
|
+
global: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function createRvsServer(options) {
|
|
71
|
+
if (!options.serverPrivateKey) {
|
|
72
|
+
throw new Error('RvsModule: serverPrivateKey 缺失。私钥仅允许来自环境变量 / KMS,禁止硬编码');
|
|
73
|
+
}
|
|
74
|
+
const processModel = (0, core_2.detectProcessModel)();
|
|
75
|
+
const replayStore = options.replayStore ?? (options.redis ? (0, rand_vec_sig_1.createRedisReplayStore)(options.redis) : undefined);
|
|
76
|
+
if (!replayStore && processModel.isCluster) {
|
|
77
|
+
core_2.alertBus.error('rvs:replay-store-per-process', '多实例未配置共享防重放存储:跨实例重放无法拦截', {
|
|
78
|
+
instances: processModel.instances,
|
|
79
|
+
runtime: processModel.runtime,
|
|
80
|
+
hint: 'RvsModule 传入 redis(共享 ioredis 实例)',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (options.mode === 'dynamic' && processModel.isCluster) {
|
|
84
|
+
core_2.logger.warn('rvs dynamic mode with multiple instances: sessSeed 仅存在进程内存', {
|
|
85
|
+
instances: processModel.instances,
|
|
86
|
+
hint: '开启粘性路由,或依赖客户端 410 重试自愈(客户端已内置)',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const server = (0, rand_vec_sig_1.createRandVecSigServer)({
|
|
90
|
+
mode: options.mode,
|
|
91
|
+
serverPrivateKey: options.serverPrivateKey,
|
|
92
|
+
versionSeed: options.versionSeed,
|
|
93
|
+
versionSeeds: options.versionSeeds,
|
|
94
|
+
deploymentId: options.deploymentId,
|
|
95
|
+
identifyHeaders: options.identifyHeaders,
|
|
96
|
+
profile: options.profile,
|
|
97
|
+
allowedPayloadModes: options.allowedPayloadModes,
|
|
98
|
+
timestampToleranceMs: options.timestampToleranceMs,
|
|
99
|
+
nonceTtlMs: options.nonceTtlMs,
|
|
100
|
+
strictSubgroupCheck: options.strictSubgroupCheck,
|
|
101
|
+
responseEncryption: options.responseEncryption,
|
|
102
|
+
session: options.session,
|
|
103
|
+
replayStore,
|
|
104
|
+
logger: options.logger ??
|
|
105
|
+
((event, detail) => core_2.logger.debug(`rvs ${event}`, detail)),
|
|
106
|
+
});
|
|
107
|
+
// 四阶段优雅关闭的资源释放阶段:释放会话定时器与存储连接
|
|
108
|
+
(0, core_2.registerResourceCloser)('rvs-server', () => server.close());
|
|
109
|
+
core_2.logger.info('rvs module initialized', {
|
|
110
|
+
mode: options.mode,
|
|
111
|
+
responseEncryption: Boolean(options.responseEncryption),
|
|
112
|
+
unprotected: options.unprotected,
|
|
113
|
+
replayStore: replayStore ? 'shared' : 'memory',
|
|
114
|
+
sessionEndpoint: options.sessionEndpoint ?? null,
|
|
115
|
+
instances: processModel.instances,
|
|
116
|
+
});
|
|
117
|
+
return server;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=rvs.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.module.js","sourceRoot":"","sources":["../src/rvs.module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type OnApplicationBootstrap, type Type } from '@nestjs/common';
|
|
2
|
+
import { DiscoveryService, Reflector } from '@nestjs/core';
|
|
3
|
+
/** 路由路径转正则:支持 :param 与 * 通配;允许全局前缀(如 /api/health/live 命中 health/live) */
|
|
4
|
+
export declare function pathToRegex(routePath: string): RegExp;
|
|
5
|
+
interface ControllerLike {
|
|
6
|
+
instance?: object;
|
|
7
|
+
metatype?: Type<unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 扫描控制器元数据,收集 @RvsSkip / @RvsForce 标记的路由正则。
|
|
11
|
+
* 抽成纯函数以便测试;Nest 容器相关逻辑全部在 RvsRouteRegistry。
|
|
12
|
+
*/
|
|
13
|
+
export declare function collectRvsRouteMatchers(controllers: ControllerLike[], reflector: Pick<Reflector, 'get'>): {
|
|
14
|
+
skip: RegExp[];
|
|
15
|
+
force: RegExp[];
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 装饰器路由注册表:应用启动完成后扫描一次。
|
|
19
|
+
* 中间件在请求期查询;启动完成前不会有请求,因此时序安全。
|
|
20
|
+
*/
|
|
21
|
+
export declare class RvsRouteRegistry implements OnApplicationBootstrap {
|
|
22
|
+
private readonly discovery;
|
|
23
|
+
private readonly reflector;
|
|
24
|
+
private skipMatchers;
|
|
25
|
+
private forceMatchers;
|
|
26
|
+
constructor(discovery: DiscoveryService, reflector: Reflector);
|
|
27
|
+
onApplicationBootstrap(): void;
|
|
28
|
+
isSkipped(url: string): boolean;
|
|
29
|
+
isForced(url: string): boolean;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RvsRouteRegistry = void 0;
|
|
13
|
+
exports.pathToRegex = pathToRegex;
|
|
14
|
+
exports.collectRvsRouteMatchers = collectRvsRouteMatchers;
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const constants_1 = require("@nestjs/common/constants");
|
|
17
|
+
const core_1 = require("@nestjs/core");
|
|
18
|
+
const rvs_decorators_1 = require("./rvs.decorators");
|
|
19
|
+
/** 路由路径转正则:支持 :param 与 * 通配;允许全局前缀(如 /api/health/live 命中 health/live) */
|
|
20
|
+
function pathToRegex(routePath) {
|
|
21
|
+
const normalized = routePath.replace(/^\/+|\/+$/g, '');
|
|
22
|
+
if (!normalized)
|
|
23
|
+
return /^.*$/;
|
|
24
|
+
const pattern = normalized
|
|
25
|
+
.split('/')
|
|
26
|
+
.map((segment) => {
|
|
27
|
+
if (segment === '*')
|
|
28
|
+
return '.*';
|
|
29
|
+
if (segment.startsWith(':'))
|
|
30
|
+
return '[^/]+';
|
|
31
|
+
return segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
+
})
|
|
33
|
+
.join('/');
|
|
34
|
+
return new RegExp(`(?:^|/)${pattern}(?:/)?(?:\\?|#|$)`);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 扫描控制器元数据,收集 @RvsSkip / @RvsForce 标记的路由正则。
|
|
38
|
+
* 抽成纯函数以便测试;Nest 容器相关逻辑全部在 RvsRouteRegistry。
|
|
39
|
+
*/
|
|
40
|
+
function collectRvsRouteMatchers(controllers, reflector) {
|
|
41
|
+
const skip = [];
|
|
42
|
+
const force = [];
|
|
43
|
+
for (const wrapper of controllers) {
|
|
44
|
+
const { instance, metatype } = wrapper;
|
|
45
|
+
if (!instance || !metatype)
|
|
46
|
+
continue;
|
|
47
|
+
const classPath = Reflect.getMetadata(constants_1.PATH_METADATA, metatype) ?? '';
|
|
48
|
+
const classSkip = reflector.get(rvs_decorators_1.RVS_SKIP_METADATA, metatype);
|
|
49
|
+
const classForce = reflector.get(rvs_decorators_1.RVS_FORCE_METADATA, metatype);
|
|
50
|
+
const proto = Object.getPrototypeOf(instance);
|
|
51
|
+
for (const methodName of Object.getOwnPropertyNames(proto)) {
|
|
52
|
+
if (methodName === 'constructor')
|
|
53
|
+
continue;
|
|
54
|
+
const handler = proto[methodName];
|
|
55
|
+
if (typeof handler !== 'function')
|
|
56
|
+
continue;
|
|
57
|
+
const methodPath = Reflect.getMetadata(constants_1.PATH_METADATA, handler);
|
|
58
|
+
if (methodPath === undefined)
|
|
59
|
+
continue; // 非路由方法
|
|
60
|
+
const joined = [classPath, methodPath].filter((p) => p !== undefined && p !== '').join('/');
|
|
61
|
+
const forceHit = classForce || reflector.get(rvs_decorators_1.RVS_FORCE_METADATA, handler);
|
|
62
|
+
const skipHit = classSkip || reflector.get(rvs_decorators_1.RVS_SKIP_METADATA, handler);
|
|
63
|
+
if (forceHit)
|
|
64
|
+
force.push(pathToRegex(joined));
|
|
65
|
+
else if (skipHit)
|
|
66
|
+
skip.push(pathToRegex(joined));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { skip, force };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 装饰器路由注册表:应用启动完成后扫描一次。
|
|
73
|
+
* 中间件在请求期查询;启动完成前不会有请求,因此时序安全。
|
|
74
|
+
*/
|
|
75
|
+
let RvsRouteRegistry = class RvsRouteRegistry {
|
|
76
|
+
constructor(discovery, reflector) {
|
|
77
|
+
this.discovery = discovery;
|
|
78
|
+
this.reflector = reflector;
|
|
79
|
+
this.skipMatchers = [];
|
|
80
|
+
this.forceMatchers = [];
|
|
81
|
+
}
|
|
82
|
+
onApplicationBootstrap() {
|
|
83
|
+
const { skip, force } = collectRvsRouteMatchers(this.discovery.getControllers(), this.reflector);
|
|
84
|
+
this.skipMatchers = skip;
|
|
85
|
+
this.forceMatchers = force;
|
|
86
|
+
}
|
|
87
|
+
isSkipped(url) {
|
|
88
|
+
return this.skipMatchers.some((re) => re.test(url));
|
|
89
|
+
}
|
|
90
|
+
isForced(url) {
|
|
91
|
+
return this.forceMatchers.some((re) => re.test(url));
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
exports.RvsRouteRegistry = RvsRouteRegistry;
|
|
95
|
+
exports.RvsRouteRegistry = RvsRouteRegistry = __decorate([
|
|
96
|
+
(0, common_1.Injectable)(),
|
|
97
|
+
__metadata("design:paramtypes", [core_1.DiscoveryService,
|
|
98
|
+
core_1.Reflector])
|
|
99
|
+
], RvsRouteRegistry);
|
|
100
|
+
//# sourceMappingURL=rvs.registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.registry.js","sourceRoot":"","sources":["../src/rvs.registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RvsServer, SessionSeed, VerifyInput, VerifyResult } from 'rand-vec-sig';
|
|
2
|
+
import type { RvsContext } from './rvs.types';
|
|
3
|
+
/**
|
|
4
|
+
* RvsService:业务侧访问 RVS 能力的唯一出口。
|
|
5
|
+
* 直接暴露 server 供高级用法(自定义 verify 流程 / internals),常规路径用封装方法。
|
|
6
|
+
*/
|
|
7
|
+
export declare class RvsService {
|
|
8
|
+
readonly server: RvsServer;
|
|
9
|
+
constructor(server: RvsServer);
|
|
10
|
+
/** 手动验签(自定义中间件/网关场景);不抛异常,失败经 result.ok 判断 */
|
|
11
|
+
verify(input: VerifyInput): Promise<VerifyResult>;
|
|
12
|
+
/** 动态模式:签发会话种子(宿主自定义下发渠道时使用) */
|
|
13
|
+
issueSession(options?: {
|
|
14
|
+
ttlMs?: number;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
}): SessionSeed;
|
|
17
|
+
/** 动态模式:吊销会话(登出等场景) */
|
|
18
|
+
revokeSession(sessionId: string): void;
|
|
19
|
+
/** 加密响应体(自定义响应出口时使用) */
|
|
20
|
+
encryptResponse(payload: unknown, context: RvsContext): Record<string, unknown>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RvsService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const rvs_tokens_1 = require("./rvs.tokens");
|
|
18
|
+
/**
|
|
19
|
+
* RvsService:业务侧访问 RVS 能力的唯一出口。
|
|
20
|
+
* 直接暴露 server 供高级用法(自定义 verify 流程 / internals),常规路径用封装方法。
|
|
21
|
+
*/
|
|
22
|
+
let RvsService = class RvsService {
|
|
23
|
+
constructor(server) {
|
|
24
|
+
this.server = server;
|
|
25
|
+
}
|
|
26
|
+
/** 手动验签(自定义中间件/网关场景);不抛异常,失败经 result.ok 判断 */
|
|
27
|
+
verify(input) {
|
|
28
|
+
return this.server.verify(input);
|
|
29
|
+
}
|
|
30
|
+
/** 动态模式:签发会话种子(宿主自定义下发渠道时使用) */
|
|
31
|
+
issueSession(options) {
|
|
32
|
+
return this.server.issueSession(options);
|
|
33
|
+
}
|
|
34
|
+
/** 动态模式:吊销会话(登出等场景) */
|
|
35
|
+
revokeSession(sessionId) {
|
|
36
|
+
this.server.revokeSession(sessionId);
|
|
37
|
+
}
|
|
38
|
+
/** 加密响应体(自定义响应出口时使用) */
|
|
39
|
+
encryptResponse(payload, context) {
|
|
40
|
+
return this.server.encryptResponse(payload, context);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
exports.RvsService = RvsService;
|
|
44
|
+
exports.RvsService = RvsService = __decorate([
|
|
45
|
+
(0, common_1.Injectable)(),
|
|
46
|
+
__param(0, (0, common_1.Inject)(rvs_tokens_1.RVS_SERVER)),
|
|
47
|
+
__metadata("design:paramtypes", [Object])
|
|
48
|
+
], RvsService);
|
|
49
|
+
//# sourceMappingURL=rvs.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.service.js","sourceRoot":"","sources":["../src/rvs.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoD;AAEpD,6CAA0C;AAG1C;;;GAGG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,YAAyC,MAAiB;QAAjB,WAAM,GAAN,MAAM,CAAW;IAAG,CAAC;IAE9D,8CAA8C;IAC9C,MAAM,CAAC,KAAkB;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,gCAAgC;IAChC,YAAY,CAAC,OAAgD;QAC3D,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,uBAAuB;IACvB,aAAa,CAAC,SAAiB;QAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,wBAAwB;IACxB,eAAe,CAAC,OAAgB,EAAE,OAAmB;QACnD,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC;CACF,CAAA;AAtBY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;IAEE,WAAA,IAAA,eAAM,EAAC,uBAAU,CAAC,CAAA;;GADpB,UAAU,CAsBtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.tokens.js","sourceRoot":"","sources":["../src/rvs.tokens.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG,oBAAoB,CAAC;AAClC,QAAA,WAAW,GAAG,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RedisLikeClient, ReplayStore, ServerRvsConfig, VerifiedRequestContext } from 'rand-vec-sig';
|
|
2
|
+
export type RvsUnprotectedMode = 'strict' | 'tolerant';
|
|
3
|
+
/**
|
|
4
|
+
* RvsModule 装配选项。
|
|
5
|
+
* 除 skipPaths / unprotected / contextProperty / sessionEndpoint / redis 外,
|
|
6
|
+
* 其余字段原样透传给 rand-vec-sig 的 createRandVecSigServer。
|
|
7
|
+
*/
|
|
8
|
+
export interface RvsModuleOptions {
|
|
9
|
+
/** static(构建期种子,零握手)| dynamic(服务端会话种子,900s,需粘性路由或 410 重试自愈) */
|
|
10
|
+
mode: 'static' | 'dynamic';
|
|
11
|
+
/** 服务端长期私钥 Rsk。仅允许来自 env / KMS,禁止硬编码与入库 */
|
|
12
|
+
serverPrivateKey: string | bigint;
|
|
13
|
+
/** static 模式版本种子;轮换窗口内可注册两个 */
|
|
14
|
+
versionSeed?: string;
|
|
15
|
+
versionSeeds?: string[];
|
|
16
|
+
/** 部署/租户标识:跨部署重放防护(同种子不同部署) */
|
|
17
|
+
deploymentId?: string;
|
|
18
|
+
identifyHeaders?: ServerRvsConfig['identifyHeaders'];
|
|
19
|
+
/** 混淆档案,必须与客户端完全一致 */
|
|
20
|
+
profile?: ServerRvsConfig['profile'];
|
|
21
|
+
allowedPayloadModes?: ServerRvsConfig['allowedPayloadModes'];
|
|
22
|
+
timestampToleranceMs?: number;
|
|
23
|
+
nonceTtlMs?: number;
|
|
24
|
+
strictSubgroupCheck?: boolean;
|
|
25
|
+
/** 响应加密(客户端需同时开启 responseEncryption) */
|
|
26
|
+
responseEncryption?: boolean;
|
|
27
|
+
session?: ServerRvsConfig['session'];
|
|
28
|
+
/** 多实例防重放:传入共享 ioredis 实例(生产必配);与 replayStore 二选一 */
|
|
29
|
+
redis?: RedisLikeClient;
|
|
30
|
+
/** 自定义防重放存储(优先于 redis) */
|
|
31
|
+
replayStore?: ReplayStore;
|
|
32
|
+
/** 跳过验签的路径:字符串为前缀匹配,RegExp 为正则匹配 */
|
|
33
|
+
skipPaths?: Array<string | RegExp>;
|
|
34
|
+
/**
|
|
35
|
+
* dynamic 模式的会话签发端点(客户端用 /rvs/session 拉取 sessSeed)。
|
|
36
|
+
* 默认 '/rvs/session';null 关闭内置端点(宿主自行下发)。static 模式恒为 null。
|
|
37
|
+
*/
|
|
38
|
+
sessionEndpoint?: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* 未携带 RVS 标识头的明文请求如何处理:
|
|
41
|
+
* - strict:拒绝(默认生产)——报文加密是数据面唯一入口
|
|
42
|
+
* - tolerant:放行(默认非生产)——便于 curl / Swagger 调试;已带标识头但验签失败的请求仍拒绝
|
|
43
|
+
*/
|
|
44
|
+
unprotected?: RvsUnprotectedMode;
|
|
45
|
+
/** 验签上下文挂载属性名,默认 'rvs' */
|
|
46
|
+
contextProperty?: string;
|
|
47
|
+
/** 结构化日志(不会收到密钥材料) */
|
|
48
|
+
logger?: ServerRvsConfig['logger'];
|
|
49
|
+
}
|
|
50
|
+
export interface ResolvedRvsOptions extends RvsModuleOptions {
|
|
51
|
+
contextProperty: string;
|
|
52
|
+
unprotected: RvsUnprotectedMode;
|
|
53
|
+
skipPaths: Array<string | RegExp>;
|
|
54
|
+
sessionEndpoint: string | null;
|
|
55
|
+
}
|
|
56
|
+
/** 验签上下文(rand-vec-sig VerifiedRequestContext 的语义别名;含密钥材料,禁止日志输出) */
|
|
57
|
+
export type RvsContext = VerifiedRequestContext;
|
|
58
|
+
export declare function resolveRvsOptions(options: RvsModuleOptions): ResolvedRvsOptions;
|
|
59
|
+
/** 字符串按前缀匹配(与 rand-vec-sig 中间件语义一致),RegExp 按正则匹配 */
|
|
60
|
+
export declare function matchesAnyPath(patterns: Array<string | RegExp>, url: string): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRvsOptions = resolveRvsOptions;
|
|
4
|
+
exports.matchesAnyPath = matchesAnyPath;
|
|
5
|
+
function resolveRvsOptions(options) {
|
|
6
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
7
|
+
const sessionEndpoint = options.sessionEndpoint === undefined ? '/rvs/session' : options.sessionEndpoint;
|
|
8
|
+
return {
|
|
9
|
+
...options,
|
|
10
|
+
contextProperty: options.contextProperty ?? 'rvs',
|
|
11
|
+
unprotected: options.unprotected ?? (isProd ? 'strict' : 'tolerant'),
|
|
12
|
+
skipPaths: [...(options.skipPaths ?? [])],
|
|
13
|
+
sessionEndpoint: options.mode === 'dynamic' ? sessionEndpoint : null,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** 字符串按前缀匹配(与 rand-vec-sig 中间件语义一致),RegExp 按正则匹配 */
|
|
17
|
+
function matchesAnyPath(patterns, url) {
|
|
18
|
+
return patterns.some((pattern) => typeof pattern === 'string' ? url === pattern || url.startsWith(pattern) : pattern.test(url));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=rvs.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvs.types.js","sourceRoot":"","sources":["../src/rvs.types.ts"],"names":[],"mappings":";;AA8DA,8CAUC;AAGD,wCAIC;AAjBD,SAAgB,iBAAiB,CAAC,OAAyB;IACzD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IACrD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IACzG,OAAO;QACL,GAAG,OAAO;QACV,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;QACjD,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;QACpE,SAAS,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACzC,eAAe,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;KACrE,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,SAAgB,cAAc,CAAC,QAAgC,EAAE,GAAW;IAC1E,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC/B,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAC7F,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@antares-core/rvs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "rand-vec-sig 的 NestJS 适配层:全局验签/解密中间件、响应加密管线、装饰器粒度控制、Redis 防重放与会话端点",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"reflect-metadata": "^0.2.2"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"@nestjs/common": "^10.3.7",
|
|
15
|
+
"@nestjs/core": "^10.3.7",
|
|
16
|
+
"rand-vec-sig": "^1.0.0",
|
|
17
|
+
"rxjs": "^7.8.1",
|
|
18
|
+
"@antares-core/core": "^2.2.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@nestjs/common": "^10.3.7",
|
|
22
|
+
"@nestjs/core": "^10.3.7",
|
|
23
|
+
"@nestjs/platform-express": "^10.4.15",
|
|
24
|
+
"@types/express": "^4.17.21",
|
|
25
|
+
"@types/node": "^20.12.12",
|
|
26
|
+
"rand-vec-sig": "^1.0.0",
|
|
27
|
+
"reflect-metadata": "^0.2.2",
|
|
28
|
+
"rimraf": "^5.0.7",
|
|
29
|
+
"rxjs": "^7.8.1",
|
|
30
|
+
"typescript": "^5.4.5",
|
|
31
|
+
"vitest": "^2.1.8",
|
|
32
|
+
"@antares-core/core": "2.2.0"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"clean": "rm -rf dist"
|
|
41
|
+
}
|
|
42
|
+
}
|