zmdms-utils 0.0.96 → 0.0.97

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/dist/es/file.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getPreToken } from './auth.js';
2
2
  import { dangerouslyXss, specialString, getFileExtension, isImageExtension } from './common.js';
3
3
  import { BASIC_KEY, TOKEN_KEY, FILE_SERVICE, FILE_PRVIEW_SERVICE_URL } from './constants.js';
4
- import { Crypto } from './crypto.js';
4
+ import { getOssCrypto } from './securityKey.js';
5
5
 
6
6
  /**
7
7
  * 附件相关接口
@@ -418,7 +418,10 @@ function getBasicInfo(baseURL, token) {
418
418
  */
419
419
  function encodeFileId(fileId, encodeNum) {
420
420
  if (encodeNum === void 0) { encodeNum = 1; }
421
- var crypto = new Crypto(window.__CRYPTO_AES_KEY__, window.__CRYPTO_DES_KEY__);
421
+ var crypto = getOssCrypto();
422
+ if (!crypto) {
423
+ throw new Error("缺少附件加密密钥,请先调用initSecurityKeys初始化或设置window.__CRYPTO_AES_KEY__");
424
+ }
422
425
  if (encodeNum === 0) {
423
426
  return crypto.encrypt(fileId);
424
427
  }
@@ -0,0 +1,49 @@
1
+ import { Crypto } from './crypto.js';
2
+
3
+ /**
4
+ * 密钥集合
5
+ */
6
+ interface ISecurityKeys {
7
+ /** 接口请求参数加密用 AES 密钥 */
8
+ apiAesKey: string;
9
+ /** 接口请求参数加密用 DES 密钥 */
10
+ apiDesKey: string;
11
+ /** 附件(OSS)相关加密用 AES 密钥 */
12
+ ossAesKey: string;
13
+ /** 附件(OSS)相关加密用 DES 密钥 */
14
+ ossDesKey: string;
15
+ }
16
+ interface ISecurityKeysOptions {
17
+ /** 接口请求的前缀地址 必传 */
18
+ baseURL: string;
19
+ }
20
+ /**
21
+ * 初始化安全密钥
22
+ * 应用启动时(render之前)调用一次 并发调用会共享同一个请求
23
+ * @param options
24
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
25
+ * @returns 密钥集合
26
+ */
27
+ declare function initSecurityKeys(options: ISecurityKeysOptions): Promise<ISecurityKeys>;
28
+ /**
29
+ * 强制刷新安全密钥
30
+ * 后端轮换密钥后可调用 会忽略已有缓存重新请求
31
+ * @param options
32
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
33
+ * @returns 密钥集合
34
+ */
35
+ declare function refreshSecurityKeys(options: ISecurityKeysOptions): Promise<ISecurityKeys>;
36
+ /**
37
+ * 获取接口请求参数加密的Crypto实例
38
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
39
+ * @returns Crypto实例 未获取到密钥时返回undefined
40
+ */
41
+ declare function getApiCrypto(): Crypto | undefined;
42
+ /**
43
+ * 获取附件(OSS)相关加密的Crypto实例
44
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
45
+ * @returns Crypto实例 未获取到密钥时返回undefined
46
+ */
47
+ declare function getOssCrypto(): Crypto | undefined;
48
+
49
+ export { ISecurityKeys, getApiCrypto, getOssCrypto, initSecurityKeys, refreshSecurityKeys };
@@ -0,0 +1,121 @@
1
+ import { __awaiter, __generator } from './_virtual/_tslib.js';
2
+ import axios from 'axios';
3
+ import { getPreToken } from './auth.js';
4
+ import { TOKEN_KEY, BASIC_KEY } from './constants.js';
5
+ import { Crypto } from './crypto.js';
6
+
7
+ // 密钥内存缓存 不挂载到window 避免产生新的明文全局变量
8
+ var securityKeys = null;
9
+ // 进行中的密钥请求 并发调用initSecurityKeys/refreshSecurityKeys时共享同一个请求
10
+ var securityKeysPromise = null;
11
+ /**
12
+ * 初始化安全密钥
13
+ * 应用启动时(render之前)调用一次 并发调用会共享同一个请求
14
+ * @param options
15
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
16
+ * @returns 密钥集合
17
+ */
18
+ function initSecurityKeys(options) {
19
+ if (securityKeys) {
20
+ return Promise.resolve(securityKeys);
21
+ }
22
+ if (securityKeysPromise) {
23
+ return securityKeysPromise;
24
+ }
25
+ return requestSecurityKeys(options);
26
+ }
27
+ /**
28
+ * 强制刷新安全密钥
29
+ * 后端轮换密钥后可调用 会忽略已有缓存重新请求
30
+ * @param options
31
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
32
+ * @returns 密钥集合
33
+ */
34
+ function refreshSecurityKeys(options) {
35
+ return requestSecurityKeys(options);
36
+ }
37
+ /**
38
+ * 获取接口请求参数加密的Crypto实例
39
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
40
+ * @returns Crypto实例 未获取到密钥时返回undefined
41
+ */
42
+ function getApiCrypto() {
43
+ if (securityKeys === null || securityKeys === void 0 ? void 0 : securityKeys.apiAesKey) {
44
+ return new Crypto(securityKeys.apiAesKey, securityKeys.apiDesKey);
45
+ }
46
+ return createCryptoFromWindow();
47
+ }
48
+ /**
49
+ * 获取附件(OSS)相关加密的Crypto实例
50
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
51
+ * @returns Crypto实例 未获取到密钥时返回undefined
52
+ */
53
+ function getOssCrypto() {
54
+ if (securityKeys === null || securityKeys === void 0 ? void 0 : securityKeys.ossAesKey) {
55
+ return new Crypto(securityKeys.ossAesKey, securityKeys.ossDesKey);
56
+ }
57
+ return createCryptoFromWindow();
58
+ }
59
+ /**
60
+ * 请求密钥并缓存
61
+ */
62
+ function requestSecurityKeys(options) {
63
+ securityKeysPromise = fetchSecurityKeys(options).then(function (keys) {
64
+ securityKeys = keys;
65
+ securityKeysPromise = null;
66
+ return keys;
67
+ }, function (err) {
68
+ securityKeysPromise = null;
69
+ throw err;
70
+ });
71
+ return securityKeysPromise;
72
+ }
73
+ /**
74
+ * 请求密钥接口
75
+ * 使用裸axios请求(不走AxiosRequest) 避免请求本身依赖密钥或触发统一的异常处理
76
+ */
77
+ function fetchSecurityKeys(options) {
78
+ var _a, _b;
79
+ return __awaiter(this, void 0, void 0, function () {
80
+ var apiBaseURL, headers, token, response, _c, code, data, msg;
81
+ return __generator(this, function (_d) {
82
+ switch (_d.label) {
83
+ case 0:
84
+ apiBaseURL = ((_b = (_a = options.baseURL) === null || _a === void 0 ? void 0 : _a.endsWith) === null || _b === void 0 ? void 0 : _b.call(_a, "/"))
85
+ ? options.baseURL.slice(0, options.baseURL.length - 1)
86
+ : options.baseURL;
87
+ headers = {};
88
+ token = getPreToken();
89
+ if (token) {
90
+ headers[TOKEN_KEY] = token;
91
+ }
92
+ return [4 /*yield*/, axios.get("".concat(apiBaseURL, "/api/").concat(BASIC_KEY, "-resource/resource/get-security-key"), { headers: headers, timeout: 60000 })];
93
+ case 1:
94
+ response = _d.sent();
95
+ _c = (response === null || response === void 0 ? void 0 : response.data) || {}, code = _c.code, data = _c.data, msg = _c.msg;
96
+ if (code === 200 && data) {
97
+ return [2 /*return*/, {
98
+ apiAesKey: data.apiAesKey,
99
+ apiDesKey: data.apiDesKey,
100
+ ossAesKey: data.ossAesKey,
101
+ ossDesKey: data.ossDesKey,
102
+ }];
103
+ }
104
+ throw new Error(msg || "获取安全密钥失败");
105
+ }
106
+ });
107
+ });
108
+ }
109
+ /**
110
+ * 从window旧密钥创建Crypto实例 兼容未改造的应用
111
+ */
112
+ function createCryptoFromWindow() {
113
+ var aesKey = window.__CRYPTO_AES_KEY__;
114
+ var desKey = window.__CRYPTO_DES_KEY__;
115
+ if (aesKey) {
116
+ return new Crypto(aesKey, desKey);
117
+ }
118
+ return undefined;
119
+ }
120
+
121
+ export { getApiCrypto, getOssCrypto, initSecurityKeys, refreshSecurityKeys };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { getPreToken, getToken, parseToken, removeToken, setToken } from './es/a
5
5
  export { AxiosRequest, IOptions, IOtherOptions } from './es/request.js';
6
6
  export { getBaseUrl } from './es/baseUrl.js';
7
7
  export { Crypto, md5 } from './es/crypto.js';
8
+ export { ISecurityKeys, getApiCrypto, getOssCrypto, initSecurityKeys, refreshSecurityKeys } from './es/securityKey.js';
8
9
  export { addThousedSeparator, appendQueryParamsToUrl, copyToClipboard, dangerouslyXss, delay, emitter, getFileExtension, isChinese, isImageExtension, isNonEmpty, parseJoin, parseQueryParams, specialString, unescapeString } from './es/common.js';
9
10
  export { divide, exactRound, formatUnit, minus, plus, times } from './es/math.js';
10
11
  export { validatePassword } from './es/passwordValidate.js';
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { getPreToken, getToken, parseToken, removeToken, setToken } from './es/a
5
5
  export { AxiosRequest } from './es/request.js';
6
6
  export { getBaseUrl } from './es/baseUrl.js';
7
7
  export { Crypto, md5 } from './es/crypto.js';
8
+ export { getApiCrypto, getOssCrypto, initSecurityKeys, refreshSecurityKeys } from './es/securityKey.js';
8
9
  export { addThousedSeparator, appendQueryParamsToUrl, copyToClipboard, dangerouslyXss, delay, emitter, getFileExtension, isChinese, isImageExtension, isNonEmpty, parseJoin, parseQueryParams, specialString, unescapeString } from './es/common.js';
9
10
  export { divide, exactRound, formatUnit, minus, plus, times } from './es/math.js';
10
11
  export { validatePassword } from './es/passwordValidate.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zmdms-utils",
3
- "version": "0.0.96",
3
+ "version": "0.0.97",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
package/src/file.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  FILE_PRVIEW_SERVICE_URL,
16
16
  } from "./constants";
17
17
  import { AxiosResponse } from "axios";
18
- import { Crypto } from "./crypto";
18
+ import { getOssCrypto } from "./securityKey";
19
19
 
20
20
  // 是否是中拓的附件服务
21
21
  const isZmdmsFileService = FILE_SERVICE === "zmdms";
@@ -546,10 +546,12 @@ interface IThumbnailLinkOptions extends Partial<IOptions> {
546
546
  * 3. 如果参数是通过body传输的,只需要encode一次
547
547
  */
548
548
  export function encodeFileId(fileId: string, encodeNum: number = 1) {
549
- const crypto = new Crypto(
550
- (window as any).__CRYPTO_AES_KEY__,
551
- (window as any).__CRYPTO_DES_KEY__,
552
- );
549
+ const crypto = getOssCrypto();
550
+ if (!crypto) {
551
+ throw new Error(
552
+ "缺少附件加密密钥,请先调用initSecurityKeys初始化或设置window.__CRYPTO_AES_KEY__",
553
+ );
554
+ }
553
555
  if (encodeNum === 0) {
554
556
  return crypto.encrypt(fileId);
555
557
  }
package/src/index.ts CHANGED
@@ -11,6 +11,13 @@ export {
11
11
  export { AxiosRequest, type IOtherOptions, type IOptions } from "./request";
12
12
  export { getBaseUrl } from "./baseUrl";
13
13
  export { Crypto, md5, Base64 } from "./crypto";
14
+ export {
15
+ initSecurityKeys,
16
+ refreshSecurityKeys,
17
+ getApiCrypto,
18
+ getOssCrypto,
19
+ type ISecurityKeys,
20
+ } from "./securityKey";
14
21
  export {
15
22
  delay,
16
23
  emitter,
@@ -0,0 +1,151 @@
1
+ /**
2
+ * 安全密钥管理
3
+ * 从后端接口获取前端加解密密钥,替代打包时的硬编码密钥,避免密钥随JS泄露
4
+ */
5
+ import axios from "axios";
6
+ import { getPreToken } from "./auth";
7
+ import { BASIC_KEY, TOKEN_KEY } from "./constants";
8
+ import { Crypto } from "./crypto";
9
+
10
+ /**
11
+ * 密钥集合
12
+ */
13
+ export interface ISecurityKeys {
14
+ /** 接口请求参数加密用 AES 密钥 */
15
+ apiAesKey: string;
16
+ /** 接口请求参数加密用 DES 密钥 */
17
+ apiDesKey: string;
18
+ /** 附件(OSS)相关加密用 AES 密钥 */
19
+ ossAesKey: string;
20
+ /** 附件(OSS)相关加密用 DES 密钥 */
21
+ ossDesKey: string;
22
+ }
23
+
24
+ interface ISecurityKeysOptions {
25
+ /** 接口请求的前缀地址 必传 */
26
+ baseURL: string;
27
+ }
28
+
29
+ // 密钥内存缓存 不挂载到window 避免产生新的明文全局变量
30
+ let securityKeys: ISecurityKeys | null = null;
31
+ // 进行中的密钥请求 并发调用initSecurityKeys/refreshSecurityKeys时共享同一个请求
32
+ let securityKeysPromise: Promise<ISecurityKeys> | null = null;
33
+
34
+ /**
35
+ * 初始化安全密钥
36
+ * 应用启动时(render之前)调用一次 并发调用会共享同一个请求
37
+ * @param options
38
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
39
+ * @returns 密钥集合
40
+ */
41
+ export function initSecurityKeys(
42
+ options: ISecurityKeysOptions,
43
+ ): Promise<ISecurityKeys> {
44
+ if (securityKeys) {
45
+ return Promise.resolve(securityKeys);
46
+ }
47
+ if (securityKeysPromise) {
48
+ return securityKeysPromise;
49
+ }
50
+ return requestSecurityKeys(options);
51
+ }
52
+
53
+ /**
54
+ * 强制刷新安全密钥
55
+ * 后端轮换密钥后可调用 会忽略已有缓存重新请求
56
+ * @param options
57
+ * @param options.baseURL {string} 接口请求的前缀地址 必传
58
+ * @returns 密钥集合
59
+ */
60
+ export function refreshSecurityKeys(
61
+ options: ISecurityKeysOptions,
62
+ ): Promise<ISecurityKeys> {
63
+ return requestSecurityKeys(options);
64
+ }
65
+
66
+ /**
67
+ * 获取接口请求参数加密的Crypto实例
68
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
69
+ * @returns Crypto实例 未获取到密钥时返回undefined
70
+ */
71
+ export function getApiCrypto(): Crypto | undefined {
72
+ if (securityKeys?.apiAesKey) {
73
+ return new Crypto(securityKeys.apiAesKey, securityKeys.apiDesKey);
74
+ }
75
+ return createCryptoFromWindow();
76
+ }
77
+
78
+ /**
79
+ * 获取附件(OSS)相关加密的Crypto实例
80
+ * 未初始化时回退读取window上的旧密钥(兼容未接入initSecurityKeys的应用)
81
+ * @returns Crypto实例 未获取到密钥时返回undefined
82
+ */
83
+ export function getOssCrypto(): Crypto | undefined {
84
+ if (securityKeys?.ossAesKey) {
85
+ return new Crypto(securityKeys.ossAesKey, securityKeys.ossDesKey);
86
+ }
87
+ return createCryptoFromWindow();
88
+ }
89
+
90
+ /**
91
+ * 请求密钥并缓存
92
+ */
93
+ function requestSecurityKeys(
94
+ options: ISecurityKeysOptions,
95
+ ): Promise<ISecurityKeys> {
96
+ securityKeysPromise = fetchSecurityKeys(options).then(
97
+ (keys) => {
98
+ securityKeys = keys;
99
+ securityKeysPromise = null;
100
+ return keys;
101
+ },
102
+ (err) => {
103
+ securityKeysPromise = null;
104
+ throw err;
105
+ },
106
+ );
107
+ return securityKeysPromise;
108
+ }
109
+
110
+ /**
111
+ * 请求密钥接口
112
+ * 使用裸axios请求(不走AxiosRequest) 避免请求本身依赖密钥或触发统一的异常处理
113
+ */
114
+ async function fetchSecurityKeys(
115
+ options: ISecurityKeysOptions,
116
+ ): Promise<ISecurityKeys> {
117
+ const apiBaseURL = options.baseURL?.endsWith?.("/")
118
+ ? options.baseURL.slice(0, options.baseURL.length - 1)
119
+ : options.baseURL;
120
+ const headers: { [prop: string]: string } = {};
121
+ const token = getPreToken();
122
+ if (token) {
123
+ headers[TOKEN_KEY] = token;
124
+ }
125
+ const response = await axios.get(
126
+ `${apiBaseURL}/api/${BASIC_KEY}-resource/resource/get-security-key`,
127
+ { headers, timeout: 60000 },
128
+ );
129
+ const { code, data, msg } = response?.data || {};
130
+ if (code === 200 && data) {
131
+ return {
132
+ apiAesKey: data.apiAesKey,
133
+ apiDesKey: data.apiDesKey,
134
+ ossAesKey: data.ossAesKey,
135
+ ossDesKey: data.ossDesKey,
136
+ };
137
+ }
138
+ throw new Error(msg || "获取安全密钥失败");
139
+ }
140
+
141
+ /**
142
+ * 从window旧密钥创建Crypto实例 兼容未改造的应用
143
+ */
144
+ function createCryptoFromWindow(): Crypto | undefined {
145
+ const aesKey = (window as any).__CRYPTO_AES_KEY__;
146
+ const desKey = (window as any).__CRYPTO_DES_KEY__;
147
+ if (aesKey) {
148
+ return new Crypto(aesKey, desKey);
149
+ }
150
+ return undefined;
151
+ }