@xlt-token/express 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/README.md +141 -0
- package/dist/index.cjs +483 -0
- package/dist/index.d.cts +163 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +163 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +328 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xltorg
|
|
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/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @xlt-token/express
|
|
2
|
+
|
|
3
|
+
Express middleware adapter for xlt-token. It connects Express `req` / `res` to `@xlt-token/core` and provides login checks, route policies, route helpers, and JSON error handling.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add express @xlt-token/express
|
|
9
|
+
pnpm add -D @types/express
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import express from 'express';
|
|
16
|
+
import {
|
|
17
|
+
createXltToken,
|
|
18
|
+
MemoryStore,
|
|
19
|
+
XltMode,
|
|
20
|
+
xltErrorHandler,
|
|
21
|
+
xltMiddleware,
|
|
22
|
+
type StpInterface,
|
|
23
|
+
} from '@xlt-token/express';
|
|
24
|
+
|
|
25
|
+
const stpInterface: StpInterface = {
|
|
26
|
+
getPermissionList: async (loginId) =>
|
|
27
|
+
loginId === '1001' ? ['user:read', 'order:*'] : [],
|
|
28
|
+
getRoleList: async (loginId) => (loginId === '1001' ? ['admin'] : []),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const xlt = createXltToken({
|
|
32
|
+
config: { tokenPrefix: '' },
|
|
33
|
+
store: new MemoryStore(),
|
|
34
|
+
stpInterface,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const app = express();
|
|
38
|
+
app.use(express.json());
|
|
39
|
+
|
|
40
|
+
const api = express.Router();
|
|
41
|
+
|
|
42
|
+
api.use(
|
|
43
|
+
xltMiddleware(xlt, {
|
|
44
|
+
ignore: ['/api/auth/login', '/api/public'],
|
|
45
|
+
policies: [
|
|
46
|
+
{ match: '/api/order', permissions: { list: ['order:read'], mode: XltMode.AND } },
|
|
47
|
+
{ match: '/api/admin', roles: { list: ['admin'], mode: XltMode.AND } },
|
|
48
|
+
{ match: '/api/pay', methods: ['POST'], safeBusiness: 'pay' },
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
api.post('/auth/login', async (req, res) => {
|
|
54
|
+
const { userId = '1001' } = req.body ?? {};
|
|
55
|
+
const token = await xlt.stpLogic.login(userId);
|
|
56
|
+
res.json({ token });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
api.get('/public', (_req, res) => {
|
|
60
|
+
res.json({ ok: true });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
api.get('/me', (req, res) => {
|
|
64
|
+
res.json({ id: req.stpLoginId, token: req.stpToken });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
api.get('/order', (_req, res) => {
|
|
68
|
+
res.json({ ok: true });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.use('/api', api);
|
|
72
|
+
app.use(xltErrorHandler());
|
|
73
|
+
|
|
74
|
+
app.listen(3000);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Route Policies
|
|
78
|
+
|
|
79
|
+
Use `ignore` and `policies` when mounting `xltMiddleware` globally or at router level. Policies run before authentication, so they are the recommended Express integration path.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
api.use(
|
|
83
|
+
xltMiddleware(xlt, {
|
|
84
|
+
ignore: ['/api/auth/login'],
|
|
85
|
+
policies: [
|
|
86
|
+
{ match: '/api/me', requireLogin: true },
|
|
87
|
+
{ match: '/api/order', permissions: { list: ['order:read'], mode: XltMode.AND } },
|
|
88
|
+
{ match: '/api/report', roles: { list: ['admin', 'ops'], mode: XltMode.OR } },
|
|
89
|
+
{ match: '/api/pay', methods: ['POST'], safeBusiness: 'pay' },
|
|
90
|
+
],
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
String matchers use path-segment prefix matching. `/api/public` matches `/api/public/docs` and `/api/public?from=web`, but it does not match `/api/publicity`.
|
|
96
|
+
|
|
97
|
+
## Route Helpers
|
|
98
|
+
|
|
99
|
+
The adapter also exports `ignoreAuth()`, `requireLogin()`, `checkPermission()`, `checkRole()`, and `checkSafe()`. These helpers must run before `xltMiddleware` in the same route chain.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
app.get('/public', ignoreAuth(), xltMiddleware(xlt), (_req, res) => {
|
|
103
|
+
res.json({ ok: true });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
app.get('/order', checkPermission('order:read'), xltMiddleware(xlt), (_req, res) => {
|
|
107
|
+
res.json({ ok: true });
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Do not mount `api.use(xltMiddleware(xlt))` before route helpers and expect those helpers to affect the same request. Express runs middleware in registration order.
|
|
112
|
+
|
|
113
|
+
## Exports
|
|
114
|
+
|
|
115
|
+
- `createXltToken`
|
|
116
|
+
- `MemoryStore`
|
|
117
|
+
- `UuidStrategy`
|
|
118
|
+
- `StpLogic`
|
|
119
|
+
- `StpPermLogic`
|
|
120
|
+
- `StpUtil`
|
|
121
|
+
- `XltMode`
|
|
122
|
+
- `createExpressContext`
|
|
123
|
+
- `xltMiddleware`
|
|
124
|
+
- `xltErrorHandler`
|
|
125
|
+
- `ignoreAuth`
|
|
126
|
+
- `requireLogin`
|
|
127
|
+
- `checkPermission`
|
|
128
|
+
- `checkRole`
|
|
129
|
+
- `checkSafe`
|
|
130
|
+
- `runAuth`
|
|
131
|
+
- `shouldCheckLogin`
|
|
132
|
+
- `resolveRouteAuthMeta`
|
|
133
|
+
- `syncExpressAuthState`
|
|
134
|
+
|
|
135
|
+
## Documentation
|
|
136
|
+
|
|
137
|
+
Read the full guide at <https://xiaolangtou.github.io/xlt-token/adapters/express>.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let _xlt_token_core = require("@xlt-token/core");
|
|
3
|
+
|
|
4
|
+
//#region src/context.ts
|
|
5
|
+
/**
|
|
6
|
+
* 将 Express `req` / `res` 适配为 core 的 `HttpContext`。
|
|
7
|
+
*
|
|
8
|
+
* `state` 复用挂在 `req._xltState` 上的请求级共享对象,使同一请求多次调用拿到同一引用。
|
|
9
|
+
*/
|
|
10
|
+
function createExpressContext(req, res) {
|
|
11
|
+
return {
|
|
12
|
+
headers: { get: (name) => req.headers[name.toLowerCase()] ?? null },
|
|
13
|
+
cookies: { get: (name) => req.cookies?.[name] ?? null },
|
|
14
|
+
query: { get: (name) => req.query[name] ?? null },
|
|
15
|
+
state: req._xltState ??= {},
|
|
16
|
+
setHeader: (name, value) => {
|
|
17
|
+
res.setHeader(name, value);
|
|
18
|
+
},
|
|
19
|
+
setCookie: (name, value, options) => {
|
|
20
|
+
if (options) res.cookie(name, value, options);
|
|
21
|
+
else res.cookie(name, value);
|
|
22
|
+
},
|
|
23
|
+
raw: () => req
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/auth/resolve-route-auth-meta.ts
|
|
29
|
+
function matchPathPrefix(path, prefix) {
|
|
30
|
+
const pathname = path.split("?")[0] ?? path;
|
|
31
|
+
return prefix === "/" || pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 解析当前请求命中的路由鉴权元数据。
|
|
35
|
+
*
|
|
36
|
+
* 在 `shouldCheckLogin` 和 `runAuth` 之前调用,使用 `req.originalUrl` 作为匹配目标,
|
|
37
|
+
* 避免 Router 嵌套时 `req.path` 丢失挂载前缀。
|
|
38
|
+
*
|
|
39
|
+
* 当多条策略同时命中时,后声明的策略覆盖前者的简单字段,并合并权限/角色列表,
|
|
40
|
+
* 因此用户可先声明 `/api` 默认策略,再声明 `/api/public` 例外。
|
|
41
|
+
*/
|
|
42
|
+
function resolveRouteAuthMeta(req, options = {}) {
|
|
43
|
+
return [...(options.ignore ?? []).map((match) => ({
|
|
44
|
+
match,
|
|
45
|
+
ignore: true
|
|
46
|
+
})), ...options.policies ?? []].reduce((meta, policy) => {
|
|
47
|
+
if (!matchPolicy(req, policy)) return meta;
|
|
48
|
+
const { match: _match, methods: _methods, ...nextMeta } = policy;
|
|
49
|
+
return mergeRouteAuthMeta(meta, nextMeta);
|
|
50
|
+
}, {});
|
|
51
|
+
}
|
|
52
|
+
/** 判断单条策略是否命中当前请求。 */
|
|
53
|
+
function matchPolicy(req, policy) {
|
|
54
|
+
if (policy.methods?.length) {
|
|
55
|
+
const method = req.method.toUpperCase();
|
|
56
|
+
if (!policy.methods.map((m) => m.toUpperCase()).includes(method)) return false;
|
|
57
|
+
}
|
|
58
|
+
return (Array.isArray(policy.match) ? policy.match : [policy.match]).some((matcher) => {
|
|
59
|
+
if (typeof matcher === "function") return matcher(req);
|
|
60
|
+
if (typeof matcher === "string") return matchPathPrefix(req.originalUrl, matcher);
|
|
61
|
+
return matcher.test(req.originalUrl);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 合并两段路由元数据:
|
|
66
|
+
* - `ignore` / `requireLogin` / `safeBusiness` 等简单字段:后者覆盖前者
|
|
67
|
+
* - `permissions` / `roles`:两者都存在时合并列表,mode 取后者
|
|
68
|
+
*/
|
|
69
|
+
function mergeRouteAuthMeta(base, next) {
|
|
70
|
+
const merged = {
|
|
71
|
+
...base,
|
|
72
|
+
...next
|
|
73
|
+
};
|
|
74
|
+
if (base.permissions && next.permissions) merged.permissions = {
|
|
75
|
+
list: [...base.permissions.list, ...next.permissions.list],
|
|
76
|
+
mode: next.permissions.mode
|
|
77
|
+
};
|
|
78
|
+
if (base.roles && next.roles) merged.roles = {
|
|
79
|
+
list: [...base.roles.list, ...next.roles.list],
|
|
80
|
+
mode: next.roles.mode
|
|
81
|
+
};
|
|
82
|
+
return merged;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/auth/should-check-login.ts
|
|
87
|
+
/**
|
|
88
|
+
* 是否需要对当前请求执行登录校验。
|
|
89
|
+
*
|
|
90
|
+
* 与 NestJS `XltTokenGuard.requiresLogin` 行为一致:
|
|
91
|
+
* - 黑名单模式(`defaultCheck === true`):除被 `ignore` 标记的路由外全部校验
|
|
92
|
+
* - 白名单模式(`defaultCheck === false`):仅校验被 `requireLogin` 标记的路由
|
|
93
|
+
*
|
|
94
|
+
* 路由元数据由 `resolveRouteAuthMeta` 提前写入 `req._xltRouteMeta`。
|
|
95
|
+
*/
|
|
96
|
+
function shouldCheckLogin(req, config) {
|
|
97
|
+
const meta = req._xltRouteMeta;
|
|
98
|
+
if (config.defaultCheck) return !meta?.ignore;
|
|
99
|
+
return meta?.requireLogin ?? false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/auth/run-auth.ts
|
|
104
|
+
/**
|
|
105
|
+
* 编排登录 + 权限 + 角色 + 二级认证校验。
|
|
106
|
+
*
|
|
107
|
+
* 与 `XltTokenGuard.canActivate` 中的权限块逻辑等价:
|
|
108
|
+
* `checkLogin` 失败时抛出 `NotLoginException`,权限/角色/safe 校验失败时分别抛出对应异常。
|
|
109
|
+
*/
|
|
110
|
+
async function runAuth(xlt, httpCtx, req) {
|
|
111
|
+
const result = await xlt.stpLogic.checkLogin(httpCtx);
|
|
112
|
+
const meta = req._xltRouteMeta;
|
|
113
|
+
if (meta?.permissions && xlt.stpPermLogic) await xlt.stpPermLogic.checkPermission(result.loginId, meta.permissions.list, meta.permissions.mode);
|
|
114
|
+
if (meta?.roles && xlt.stpPermLogic) await xlt.stpPermLogic.checkRole(result.loginId, meta.roles.list, meta.roles.mode);
|
|
115
|
+
if (meta?.safeBusiness) await xlt.stpLogic.checkSafe(result.token, meta.safeBusiness);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/sync-state.ts
|
|
121
|
+
/**
|
|
122
|
+
* 将鉴权成功后写入 `ctx.state` 的登录态同步到 Express `req` 上。
|
|
123
|
+
*
|
|
124
|
+
* core 在 `_resolveLoginId` 中写入 `ctx.state.stpLoginId` / `ctx.state.stpToken`,
|
|
125
|
+
* 这里对应同步到 `req.stpLoginId` / `req.stpToken`(与 NestJS Guard 字段命名一致)。
|
|
126
|
+
*/
|
|
127
|
+
function syncExpressAuthState(req, ctx) {
|
|
128
|
+
const loginId = ctx.state.stpLoginId;
|
|
129
|
+
const token = ctx.state.stpToken;
|
|
130
|
+
if (loginId != null) req.stpLoginId = String(loginId);
|
|
131
|
+
if (token != null) req.stpToken = String(token);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/middleware/xlt-middleware.ts
|
|
136
|
+
/**
|
|
137
|
+
* 全局登录校验中间件。
|
|
138
|
+
*
|
|
139
|
+
* 执行流程:
|
|
140
|
+
* 1. `createExpressContext(req, res)`
|
|
141
|
+
* 2. `resolveRouteAuthMeta` → 写入 `req._xltRouteMeta`
|
|
142
|
+
* 3. `shouldCheckLogin` 判断是否需要校验,不需要则直接放行
|
|
143
|
+
* 4. `runAuth`(登录 + 权限 + 角色 + safe),成功后 `syncExpressAuthState`
|
|
144
|
+
* 5. 任意异常通过 `next(err)` 交给 `xltErrorHandler`
|
|
145
|
+
*/
|
|
146
|
+
function xltMiddleware(xlt, options = {}) {
|
|
147
|
+
return async (req, res, next) => {
|
|
148
|
+
const httpCtx = createExpressContext(req, res);
|
|
149
|
+
req._xltRouteMeta = {
|
|
150
|
+
...req._xltRouteMeta,
|
|
151
|
+
...resolveRouteAuthMeta(req, options)
|
|
152
|
+
};
|
|
153
|
+
if (!shouldCheckLogin(req, xlt.config)) return next();
|
|
154
|
+
try {
|
|
155
|
+
await runAuth(xlt, httpCtx, req);
|
|
156
|
+
syncExpressAuthState(req, httpCtx);
|
|
157
|
+
next();
|
|
158
|
+
} catch (err) {
|
|
159
|
+
next(err);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/middleware/ignore-auth.ts
|
|
166
|
+
/**
|
|
167
|
+
* 路由级 helper:标记当前路由忽略登录校验(黑名单模式下放行)。
|
|
168
|
+
*
|
|
169
|
+
* 仅写入 `req._xltRouteMeta`,因此必须在同一条 route chain 中位于 `xltMiddleware` 之前才有效。
|
|
170
|
+
* 推荐主路径仍是 `xltMiddleware` 的 `ignore` / `policies` 选项。
|
|
171
|
+
*/
|
|
172
|
+
function ignoreAuth() {
|
|
173
|
+
return (req, _res, next) => {
|
|
174
|
+
req._xltRouteMeta = {
|
|
175
|
+
...req._xltRouteMeta,
|
|
176
|
+
ignore: true
|
|
177
|
+
};
|
|
178
|
+
next();
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/middleware/require-login.ts
|
|
184
|
+
/**
|
|
185
|
+
* 路由级 helper:标记当前路由需要登录(白名单模式下开启校验)。
|
|
186
|
+
*
|
|
187
|
+
* 仅写入 `req._xltRouteMeta`,必须位于 `xltMiddleware` 之前才生效。
|
|
188
|
+
*/
|
|
189
|
+
function requireLogin() {
|
|
190
|
+
return (req, _res, next) => {
|
|
191
|
+
req._xltRouteMeta = {
|
|
192
|
+
...req._xltRouteMeta,
|
|
193
|
+
requireLogin: true
|
|
194
|
+
};
|
|
195
|
+
next();
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/middleware/check-permission.ts
|
|
201
|
+
/**
|
|
202
|
+
* 路由级 helper:声明当前路由所需权限。
|
|
203
|
+
*
|
|
204
|
+
* 仅写入 `req._xltRouteMeta.permissions`,必须位于 `xltMiddleware` 之前才生效。
|
|
205
|
+
*/
|
|
206
|
+
function checkPermission(permission, mode = _xlt_token_core.XltMode.AND) {
|
|
207
|
+
return (req, _res, next) => {
|
|
208
|
+
const list = Array.isArray(permission) ? permission : [permission];
|
|
209
|
+
req._xltRouteMeta = {
|
|
210
|
+
...req._xltRouteMeta,
|
|
211
|
+
permissions: {
|
|
212
|
+
list,
|
|
213
|
+
mode
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
next();
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/middleware/check-role.ts
|
|
222
|
+
/**
|
|
223
|
+
* 路由级 helper:声明当前路由所需角色。
|
|
224
|
+
*
|
|
225
|
+
* 仅写入 `req._xltRouteMeta.roles`,必须位于 `xltMiddleware` 之前才生效。
|
|
226
|
+
*/
|
|
227
|
+
function checkRole(role, mode = _xlt_token_core.XltMode.AND) {
|
|
228
|
+
return (req, _res, next) => {
|
|
229
|
+
const list = Array.isArray(role) ? role : [role];
|
|
230
|
+
req._xltRouteMeta = {
|
|
231
|
+
...req._xltRouteMeta,
|
|
232
|
+
roles: {
|
|
233
|
+
list,
|
|
234
|
+
mode
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
next();
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/middleware/check-safe.ts
|
|
243
|
+
/**
|
|
244
|
+
* 路由级 helper:声明当前路由需要二级认证安全窗口。
|
|
245
|
+
*
|
|
246
|
+
* 仅写入 `req._xltRouteMeta.safeBusiness`,必须位于 `xltMiddleware` 之前才生效。
|
|
247
|
+
*/
|
|
248
|
+
function checkSafe(business) {
|
|
249
|
+
return (req, _res, next) => {
|
|
250
|
+
req._xltRouteMeta = {
|
|
251
|
+
...req._xltRouteMeta,
|
|
252
|
+
safeBusiness: business
|
|
253
|
+
};
|
|
254
|
+
next();
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/error/map-xlt-error.ts
|
|
260
|
+
/**
|
|
261
|
+
* 将 core 鉴权异常映射为 HTTP 状态码 + JSON body。
|
|
262
|
+
* 非 xlt-token 异常返回 `null`,交由调用方继续向后传递。
|
|
263
|
+
*/
|
|
264
|
+
function mapXltError(err) {
|
|
265
|
+
if (err instanceof _xlt_token_core.NotLoginException) return {
|
|
266
|
+
status: 401,
|
|
267
|
+
body: {
|
|
268
|
+
statusCode: 401,
|
|
269
|
+
code: err.code,
|
|
270
|
+
type: err.type,
|
|
271
|
+
message: err.message,
|
|
272
|
+
token: err.token
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
if (err instanceof _xlt_token_core.NotPermissionException) return {
|
|
276
|
+
status: 403,
|
|
277
|
+
body: {
|
|
278
|
+
statusCode: 403,
|
|
279
|
+
code: err.code,
|
|
280
|
+
permission: err.permission,
|
|
281
|
+
mode: err.mode,
|
|
282
|
+
message: err.message
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
if (err instanceof _xlt_token_core.NotRoleException) return {
|
|
286
|
+
status: 403,
|
|
287
|
+
body: {
|
|
288
|
+
statusCode: 403,
|
|
289
|
+
code: err.code,
|
|
290
|
+
role: err.role,
|
|
291
|
+
mode: err.mode,
|
|
292
|
+
message: err.message
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
if (err instanceof _xlt_token_core.NotSafeException) return {
|
|
296
|
+
status: 403,
|
|
297
|
+
body: {
|
|
298
|
+
statusCode: 403,
|
|
299
|
+
code: err.code,
|
|
300
|
+
business: err.business,
|
|
301
|
+
message: err.message
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/error/xlt-error-handler.ts
|
|
309
|
+
/**
|
|
310
|
+
* 四参数 Express 错误中间件,挂在路由链末尾,将 core 鉴权异常转为 401/403 JSON。
|
|
311
|
+
* 非 xlt-token 异常透传给下一个错误处理器。
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* app.use(xltErrorHandler());
|
|
315
|
+
*/
|
|
316
|
+
function xltErrorHandler() {
|
|
317
|
+
return (err, _req, res, next) => {
|
|
318
|
+
const mapped = mapXltError(err);
|
|
319
|
+
if (!mapped) {
|
|
320
|
+
next(err);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
res.status(mapped.status).json(mapped.body);
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
//#endregion
|
|
328
|
+
Object.defineProperty(exports, 'DEFAULT_XLT_TOKEN_CONFIG', {
|
|
329
|
+
enumerable: true,
|
|
330
|
+
get: function () {
|
|
331
|
+
return _xlt_token_core.DEFAULT_XLT_TOKEN_CONFIG;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
Object.defineProperty(exports, 'MemoryStore', {
|
|
335
|
+
enumerable: true,
|
|
336
|
+
get: function () {
|
|
337
|
+
return _xlt_token_core.MemoryStore;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
Object.defineProperty(exports, 'NotLoginException', {
|
|
341
|
+
enumerable: true,
|
|
342
|
+
get: function () {
|
|
343
|
+
return _xlt_token_core.NotLoginException;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
Object.defineProperty(exports, 'NotLoginType', {
|
|
347
|
+
enumerable: true,
|
|
348
|
+
get: function () {
|
|
349
|
+
return _xlt_token_core.NotLoginType;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
Object.defineProperty(exports, 'NotPermissionException', {
|
|
353
|
+
enumerable: true,
|
|
354
|
+
get: function () {
|
|
355
|
+
return _xlt_token_core.NotPermissionException;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
Object.defineProperty(exports, 'NotRoleException', {
|
|
359
|
+
enumerable: true,
|
|
360
|
+
get: function () {
|
|
361
|
+
return _xlt_token_core.NotRoleException;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
Object.defineProperty(exports, 'NotSafeException', {
|
|
365
|
+
enumerable: true,
|
|
366
|
+
get: function () {
|
|
367
|
+
return _xlt_token_core.NotSafeException;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
Object.defineProperty(exports, 'StpLogic', {
|
|
371
|
+
enumerable: true,
|
|
372
|
+
get: function () {
|
|
373
|
+
return _xlt_token_core.StpLogic;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
Object.defineProperty(exports, 'StpPermLogic', {
|
|
377
|
+
enumerable: true,
|
|
378
|
+
get: function () {
|
|
379
|
+
return _xlt_token_core.StpPermLogic;
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
Object.defineProperty(exports, 'StpUtil', {
|
|
383
|
+
enumerable: true,
|
|
384
|
+
get: function () {
|
|
385
|
+
return _xlt_token_core.StpUtil;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
Object.defineProperty(exports, 'UuidStrategy', {
|
|
389
|
+
enumerable: true,
|
|
390
|
+
get: function () {
|
|
391
|
+
return _xlt_token_core.UuidStrategy;
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
Object.defineProperty(exports, 'XLT_STP_INTERFACE', {
|
|
395
|
+
enumerable: true,
|
|
396
|
+
get: function () {
|
|
397
|
+
return _xlt_token_core.XLT_STP_INTERFACE;
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
Object.defineProperty(exports, 'XLT_TOKEN_CONFIG', {
|
|
401
|
+
enumerable: true,
|
|
402
|
+
get: function () {
|
|
403
|
+
return _xlt_token_core.XLT_TOKEN_CONFIG;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
Object.defineProperty(exports, 'XLT_TOKEN_HOOKS', {
|
|
407
|
+
enumerable: true,
|
|
408
|
+
get: function () {
|
|
409
|
+
return _xlt_token_core.XLT_TOKEN_HOOKS;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
Object.defineProperty(exports, 'XLT_TOKEN_STORE', {
|
|
413
|
+
enumerable: true,
|
|
414
|
+
get: function () {
|
|
415
|
+
return _xlt_token_core.XLT_TOKEN_STORE;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
Object.defineProperty(exports, 'XLT_TOKEN_STRATEGY', {
|
|
419
|
+
enumerable: true,
|
|
420
|
+
get: function () {
|
|
421
|
+
return _xlt_token_core.XLT_TOKEN_STRATEGY;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
Object.defineProperty(exports, 'XltError', {
|
|
425
|
+
enumerable: true,
|
|
426
|
+
get: function () {
|
|
427
|
+
return _xlt_token_core.XltError;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
Object.defineProperty(exports, 'XltMode', {
|
|
431
|
+
enumerable: true,
|
|
432
|
+
get: function () {
|
|
433
|
+
return _xlt_token_core.XltMode;
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
Object.defineProperty(exports, 'XltSession', {
|
|
437
|
+
enumerable: true,
|
|
438
|
+
get: function () {
|
|
439
|
+
return _xlt_token_core.XltSession;
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
exports.checkPermission = checkPermission;
|
|
443
|
+
exports.checkRole = checkRole;
|
|
444
|
+
exports.checkSafe = checkSafe;
|
|
445
|
+
exports.createExpressContext = createExpressContext;
|
|
446
|
+
Object.defineProperty(exports, 'createMockHttpContext', {
|
|
447
|
+
enumerable: true,
|
|
448
|
+
get: function () {
|
|
449
|
+
return _xlt_token_core.createMockHttpContext;
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
Object.defineProperty(exports, 'createXltToken', {
|
|
453
|
+
enumerable: true,
|
|
454
|
+
get: function () {
|
|
455
|
+
return _xlt_token_core.createXltToken;
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
exports.ignoreAuth = ignoreAuth;
|
|
459
|
+
Object.defineProperty(exports, 'matchPermission', {
|
|
460
|
+
enumerable: true,
|
|
461
|
+
get: function () {
|
|
462
|
+
return _xlt_token_core.matchPermission;
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
exports.requireLogin = requireLogin;
|
|
466
|
+
exports.resolveRouteAuthMeta = resolveRouteAuthMeta;
|
|
467
|
+
exports.runAuth = runAuth;
|
|
468
|
+
Object.defineProperty(exports, 'setStpLogic', {
|
|
469
|
+
enumerable: true,
|
|
470
|
+
get: function () {
|
|
471
|
+
return _xlt_token_core.setStpLogic;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
Object.defineProperty(exports, 'setStpPermLogic', {
|
|
475
|
+
enumerable: true,
|
|
476
|
+
get: function () {
|
|
477
|
+
return _xlt_token_core.setStpPermLogic;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
exports.shouldCheckLogin = shouldCheckLogin;
|
|
481
|
+
exports.syncExpressAuthState = syncExpressAuthState;
|
|
482
|
+
exports.xltErrorHandler = xltErrorHandler;
|
|
483
|
+
exports.xltMiddleware = xltMiddleware;
|