@lingxiteam/ebe-utils 0.0.33 → 0.0.35

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.
Files changed (34) hide show
  1. package/es/index.d.ts +5 -1
  2. package/es/index.js +107 -62
  3. package/lib/h5public/src/components/factory/src/Icon/IconED/index.less +43 -0
  4. package/lib/h5public/src/components/factory/src/Icon/IconED/index.tsx +290 -0
  5. package/lib/public/src/services/api/engine.ts +0 -2
  6. package/lib/public/src/utils/Security/clientCapabilities.ts +8 -0
  7. package/lib/public/src/utils/Security/config.ts +56 -49
  8. package/lib/public/src/utils/Security/const.ts +38 -127
  9. package/lib/public/src/utils/Security/debug/data.ts +86 -0
  10. package/lib/public/src/utils/Security/debug/index.ts +21 -0
  11. package/lib/public/src/utils/Security/encipher/sign.ts +25 -56
  12. package/lib/public/src/utils/Security/html.ts +52 -0
  13. package/lib/public/src/utils/Security/httpEncryption.ts +42 -23
  14. package/lib/public/src/utils/Security/index.ts +15 -7
  15. package/lib/public/src/utils/Security/requester/fetch.ts +87 -52
  16. package/lib/public/src/utils/Security/requester/wx.ts +60 -11
  17. package/lib/public/src/utils/Security/requester/xhr.ts +194 -40
  18. package/lib/public/src/utils/Security/types.ts +8 -90
  19. package/lib/public/src/utils/Security/urlEncryption.ts +108 -0
  20. package/lib/public/src/utils/Security/utils/atob.ts +34 -0
  21. package/lib/public/src/utils/Security/utils/caseInsensitive.ts +25 -0
  22. package/lib/public/src/utils/Security/utils/check.ts +27 -3
  23. package/lib/public/src/utils/Security/utils/cookie.ts +101 -53
  24. package/lib/public/src/utils/Security/utils/encrypted.ts +190 -43
  25. package/lib/public/src/utils/Security/utils/random.ts +21 -0
  26. package/lib/public/src/utils/Security/utils/storge.ts +22 -0
  27. package/lib/public/src/utils/Security/utils/url.ts +30 -5
  28. package/lib/public/src/utils/getSceneCode.ts +51 -0
  29. package/lib/public/src/utils/useTool.ts +2 -172
  30. package/lib/public/yarn.lock +11441 -0
  31. package/package.json +3 -3
  32. package/lib/pcpublic/src/components/pcfactory/src/StdUpload/assets/closeIcon.png +0 -0
  33. package/lib/pcpublic/src/components/pcfactory/src/StdUpload/assets/fileName.png +0 -0
  34. package/lib/pcpublic/src/components/pcfactory/src/StdUpload/assets/img.png +0 -0
@@ -0,0 +1,86 @@
1
+ import { commonGlobal } from '../const';
2
+ import message from '../utils/message';
3
+ import generateRandomNumber from '../utils/random';
4
+
5
+ export type configTpe = {
6
+ globalAccess?: boolean;
7
+ storeMax?: number;
8
+ };
9
+
10
+ /**
11
+ * 存储每个请求的签名/加密数据
12
+ * 目前仅支持通过localStorage来开启数据记录,避免误将setConfig设置的debug发到生产环境
13
+ * 通过拷贝请求头里获取 reqid,在控制台粘贴该信息进行查看调试数据
14
+ */
15
+ class DebugDataStore {
16
+ private tag: string = '';
17
+
18
+ private config: configTpe = {};
19
+
20
+ private data: Record<string, any> = {};
21
+
22
+ private storeAllKeys: string[] = [];
23
+
24
+ private storeActiveKeys: string[] = [];
25
+
26
+ constructor(config: configTpe = {}) {
27
+ this.config = Object.assign(
28
+ {
29
+ globalAccess: true,
30
+ storeMax: 20,
31
+ },
32
+ config,
33
+ );
34
+
35
+ this.tag = `LXREQ${generateRandomNumber(2)}`;
36
+
37
+ const _this = this;
38
+
39
+ if (this.config.globalAccess) {
40
+ commonGlobal[this.tag] = new Proxy(this.data, {
41
+ get(target, props) {
42
+ if (typeof props === 'string') {
43
+ if (props === 'toJSON') {
44
+ return '{}';
45
+ }
46
+ return _this.getData(`${_this.tag}.${props}`);
47
+ }
48
+ return {};
49
+ },
50
+ });
51
+ }
52
+ }
53
+
54
+ public createId() {
55
+ return `${this.tag}.T${generateRandomNumber(2)}${Date.now()}`;
56
+ }
57
+
58
+ public store(id: string, key: string, value: any) {
59
+ const _id = id.replace(/(window|global|self|globalThis)\./g, '');
60
+ if (!this.storeAllKeys.includes(_id)) {
61
+ this.data[_id] = { [key]: value };
62
+ this.storeAllKeys.push(_id);
63
+ this.storeActiveKeys.push(_id);
64
+ if (
65
+ this.config.storeMax &&
66
+ this.storeActiveKeys.length > this.config.storeMax
67
+ ) {
68
+ const delkey = this.storeActiveKeys.shift();
69
+ if (delkey) {
70
+ delete this.data[delkey];
71
+ }
72
+ }
73
+ } else {
74
+ this.data[_id] = Object.assign(this.data[_id], { [key]: value });
75
+ }
76
+ }
77
+
78
+ public getData(id: string) {
79
+ if (this.storeAllKeys.includes(id) && !this.storeActiveKeys.includes(id)) {
80
+ message.warn('数据已失效');
81
+ }
82
+ return this.data[id];
83
+ }
84
+ }
85
+
86
+ export default DebugDataStore;
@@ -0,0 +1,21 @@
1
+ import storageUtil from '../utils/storge';
2
+ import type { configTpe } from './data';
3
+ import DebugDataStore from './data';
4
+
5
+ // 通过本地存储的参数开启调试
6
+ export const isAvailable = !!storageUtil.get('lxDebug');
7
+
8
+ const debug = (() => {
9
+ if (isAvailable) {
10
+ let lxDebug = storageUtil.get('lxDebug');
11
+ try {
12
+ lxDebug = JSON.parse(lxDebug as string);
13
+ } catch {
14
+ lxDebug = 'true';
15
+ }
16
+ return new DebugDataStore(lxDebug === 'true' ? {} : (lxDebug as configTpe));
17
+ }
18
+ return null;
19
+ })();
20
+
21
+ export default debug;
@@ -2,15 +2,12 @@
2
2
  import { SHA256 } from 'crypto-js';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import config from '../config';
5
- import { aesKey, securityHeaderKey, signHeaderKey } from '../const';
5
+ import { aesKey, reqIdKey } from '../const';
6
+ import debug from '../debug';
6
7
  import { MODE, signHttpOptionsType } from '../types';
7
8
  import { checkIsSignMode } from '../utils/check';
8
9
  import message from '../utils/message';
9
- import {
10
- decodeQueryString,
11
- obj2QueryString,
12
- removeURLParameters,
13
- } from '../utils/url';
10
+ import { decodeQueryString, obj2QueryString } from '../utils/url';
14
11
  import { AESEncrypt } from './aes';
15
12
 
16
13
  // 注意:修改此文件请跑测试用例, npm run test
@@ -37,7 +34,7 @@ import { AESEncrypt } from './aes';
37
34
 
38
35
  // 由于X-B 的规则可能被业务网关使用,并往请求头增加内容,导致前后端加密不一致
39
36
  // 2022.08.09 使用以下3个固定值 + X-LX-*匹配 方式
40
- const hKeys = ['X-B-AUTH', 'X-B-TARGET-ID', 'APP-ID'];
37
+ const hKeys = ['x-b-auth', 'x-b-target-id', 'app-id'];
41
38
 
42
39
  // 获取接口路径,2023-03-16调整,由于网关配置问题可能导致前后端取的服务地址不一致,调整后只取最后一级
43
40
  const getApiPath = (url: string) => {
@@ -72,13 +69,16 @@ export const createHttpSignStr = (
72
69
  let headerParams = '';
73
70
  const headersKeyArr: any = [];
74
71
  if (headers) {
75
- Object.keys(headers)
76
- .concat(config.sign?.includeHeaders || [])
77
- .forEach((key) => {
78
- if (key.startsWith('X-LX-') || hKeys.includes(key)) {
79
- headersKeyArr.push(`${key.toLowerCase()}=${headers[key]}`);
80
- }
81
- });
72
+ const includesHeaderKeys =
73
+ config.sign?.headerKeys?.map((item) => item.toLowerCase()) || hKeys;
74
+ Object.keys(headers).forEach((key) => {
75
+ if (
76
+ key.startsWith('X-LX-') ||
77
+ includesHeaderKeys.includes(key.toLowerCase())
78
+ ) {
79
+ headersKeyArr.push(`${key.toLowerCase()}=${headers[key]}`);
80
+ }
81
+ });
82
82
  }
83
83
  headerParams = headersKeyArr.sort().join(';');
84
84
 
@@ -87,10 +87,11 @@ export const createHttpSignStr = (
87
87
  // body为空时,可能出现前后端取值不一致问题。
88
88
  // nodejs 一般存在空字符串、空对象、undefined、空表单 等,取决于框架处理和使用的中间件
89
89
  // java后端一般为null
90
+ // 目前与后端约定默认为空字符串
90
91
  let finallyBody = body;
91
- if (typeof config.emptyBodyValue !== 'undefined') {
92
+ if (body === undefined || body === null) {
92
93
  finallyBody =
93
- body === undefined || body === null ? config.emptyBodyValue : body;
94
+ typeof config.emptyBodyValue !== 'undefined' ? config.emptyBodyValue : '';
94
95
  }
95
96
  if (method.toLowerCase() === 'get') {
96
97
  // 为保证与后端值一致此处需要将数据进行解码
@@ -114,7 +115,6 @@ export const createHttpSignStr = (
114
115
  let result: string = '';
115
116
  switch (signVersion) {
116
117
  case MODE.SIGN:
117
- case MODE.SIGN_KEY:
118
118
  content = [
119
119
  apiPath,
120
120
  headerParams,
@@ -165,48 +165,17 @@ export const createHttpSignStr = (
165
165
  message.log('url', url);
166
166
  message.log('str', content);
167
167
  message.log('sign', result);
168
+
169
+ if (headers?.[reqIdKey] && debug) {
170
+ debug.store(headers[reqIdKey], 'modeRequest', signVersion);
171
+ debug.store(headers[reqIdKey], 'url', url);
172
+ debug.store(headers[reqIdKey], 'str', content);
173
+ debug.store(headers[reqIdKey], 'sign', result);
174
+ }
168
175
  }
169
176
 
170
177
  // 返回约定签名
171
178
  return result;
172
179
  };
173
180
 
174
- // 由于(img、script)无法进行处理请求头,将签名参数x-sign放到url参数里
175
- export const buildXSignUrl = (
176
- url: string,
177
- options: signHttpOptionsType = {},
178
- version?: any,
179
- ) => {
180
- if (!url) {
181
- return '';
182
- }
183
- if (version && !checkIsSignMode(version)) {
184
- message.warn(`不支持[${version}]签名模式`);
185
- return url;
186
- }
187
- let _url = url;
188
- const signVersion =
189
- version || checkIsSignMode(config.mode as MODE)
190
- ? config.mode
191
- : MODE.SIGN_WITH_TIME;
192
- const signStr = createHttpSignStr(
193
- url,
194
- { method: 'GET', ...options },
195
- signVersion,
196
- );
197
- // 如果url上存在签名则移除原签名
198
- if (
199
- url.indexOf(signHeaderKey) !== -1 ||
200
- url.indexOf(securityHeaderKey) !== -1
201
- ) {
202
- _url = removeURLParameters(url, [signHeaderKey, securityHeaderKey]);
203
- }
204
- return `${_url}${
205
- _url.includes('?') ? '&' : '?'
206
- }${signHeaderKey}=${signStr}&${securityHeaderKey}=${signVersion}`;
207
- };
208
-
209
- // buildXSignUrl兼容平台内部使用,外部使用createHttpSignWithUrl
210
- export const createHttpSignWithUrl = buildXSignUrl;
211
-
212
- export default { createHttpSignStr, createHttpSignWithUrl };
181
+ export default { createHttpSignStr };
@@ -0,0 +1,52 @@
1
+ import type { securityType } from './types';
2
+
3
+ /**
4
+ * 移除内容里恶意html代码(xss攻击)
5
+ * @param str 待处理html内容
6
+ * @param options 可选配置项
7
+ * @return 安全的html片段
8
+ */
9
+ const safeHTML: typeof securityType.html.safeHTML = (str, options) => {
10
+ if (typeof str !== 'string') return str || '';
11
+ const blacklist = options?.blackList || ['script', 'style', 'iframe'];
12
+
13
+ // 创建一个新的 DOM 解析器对象
14
+ const parser = new DOMParser();
15
+
16
+ // 使用 parseFromString 方法将 HTML 片段转换为 DOM 对象
17
+ const doc = parser.parseFromString(str, 'text/html');
18
+
19
+ // 遍历 DOM 树并删除恶意节点
20
+ function cleanNode(node: Element) {
21
+ if (node.nodeType === 1) {
22
+ // 元素节点
23
+ if (blacklist.indexOf(node.nodeName.toLowerCase()) !== -1) {
24
+ node.parentNode?.removeChild(node);
25
+ return;
26
+ }
27
+
28
+ // 移除元素节点上所有事件属性
29
+ for (let i = node.attributes.length - 1; i >= 0; i -= 1) {
30
+ const attr = node.attributes[i];
31
+ if (
32
+ attr.name.startsWith('on') ||
33
+ String(node.getAttribute(attr.name))
34
+ .toLowerCase()
35
+ .indexOf('javascript:') !== -1
36
+ ) {
37
+ node.removeAttribute(attr.name);
38
+ }
39
+ }
40
+ }
41
+
42
+ for (let i = 0; i < node.childNodes.length; i += 1) {
43
+ cleanNode(node.childNodes[i] as Element);
44
+ }
45
+ }
46
+ cleanNode(doc.body);
47
+ return doc.body.innerHTML;
48
+ };
49
+
50
+ export default {
51
+ safeHTML,
52
+ };
@@ -4,14 +4,8 @@
4
4
  * @Description: 一个简单的http请求安全加密处理
5
5
  */
6
6
 
7
- // TODO 支持XMLHttpRequest拦截、参数混淆、响应拦截
8
-
9
- // 使用方式1: 使用fetch请求模块,start({ mode: signKey }),适应经过封装的fetch模块
10
- // 使用方式2: 使用fetch请求模块,将window.fetch 替换成 fetch,适应自己封装的fetch模块
11
- // 使用方式3: 不使用fetch请求模块,使用createHttpSignStr自己获取签名,自行在header上添加参数
12
- import merge from 'merge';
13
- import capabilities from './clientCapabilities';
14
- import config, { createDefaultConfig, setConfig } from './config';
7
+ import capabilities, { isBrowser } from './clientCapabilities';
8
+ import config, { setConfig } from './config';
15
9
  import {
16
10
  conflict as conflictFetch,
17
11
  noConflict as noConflictFetch,
@@ -28,22 +22,48 @@ import type { configType } from './types';
28
22
  import { checkIsModeValue } from './utils/check';
29
23
  import message from './utils/message';
30
24
 
31
- let isHttpEncryption = false;
25
+ // 在浏览器模式下可能由于多个子工程集成造成多次conflict引发异常,在浏览器里需要将开关配置全局化
26
+ const _switch: Record<string, any> = {
27
+ _isHttpEncryption: false,
28
+ };
29
+
30
+ Object.defineProperty(
31
+ _switch,
32
+ 'isHttpEncryption',
33
+ isBrowser
34
+ ? {
35
+ get() {
36
+ return (window as any).isLxHttpEncryption ?? this._isHttpEncryption;
37
+ },
38
+ set(value) {
39
+ (window as any).isLxHttpEncryption = value;
40
+ this._isHttpEncryption = value;
41
+ },
42
+ }
43
+ : {
44
+ get() {
45
+ return this._isHttpEncryption;
46
+ },
47
+ set(value) {
48
+ this._isHttpEncryption = value;
49
+ },
50
+ },
51
+ );
52
+
32
53
  const noConflictList: Function[] = [];
33
54
 
34
55
  function start(startConfig?: configType) {
35
- if (isHttpEncryption) {
36
- message.warn('安全模式已开启,请使用stop方法停止后再重新开启');
37
- return true;
56
+ if (_switch.isHttpEncryption) {
57
+ return null;
38
58
  }
39
59
 
40
- const _startConfig = merge.recursive({}, createDefaultConfig(), startConfig);
41
-
42
- const mode = _startConfig.mode as string;
43
- if (checkIsModeValue(mode)) {
44
- isHttpEncryption = true;
60
+ // const _startConfig = merge.recursive({}, createDefaultConfig(), startConfig);
61
+ const mode = startConfig?.mode;
62
+ // 如果存在设置mode,校验mode,如果为空时,使用config预置的
63
+ if (!mode || checkIsModeValue(mode)) {
64
+ _switch.isHttpEncryption = true;
45
65
  // 设置
46
- setConfig(_startConfig);
66
+ setConfig(startConfig || {});
47
67
 
48
68
  // 按需启动请求劫持
49
69
  if (capabilities.fetch && config.requester?.includes('fetch')) {
@@ -58,15 +78,14 @@ function start(startConfig?: configType) {
58
78
  conflictWxRequest();
59
79
  noConflictList.push(noConflictWxRequest);
60
80
  }
61
- } else {
62
- message.error(`http安全开启失败,不支持${mode}模式`);
81
+ return true;
63
82
  }
64
-
65
- return isHttpEncryption;
83
+ message.error(`http安全开启失败,不支持${mode}模式`);
84
+ return false;
66
85
  }
67
86
 
68
87
  function stop() {
69
- isHttpEncryption = false;
88
+ _switch.isHttpEncryption = false;
70
89
  noConflictList.forEach((noConflict) => {
71
90
  noConflict();
72
91
  });
@@ -1,28 +1,36 @@
1
- import { setConfig } from './config';
2
- import KEYS from './const';
1
+ import { getConfig, initConfig, setConfig } from './config';
3
2
  import { AESDecrypt, AESEncrypt } from './encipher/aes';
4
- import { DESDecrypt, DESEncrypt } from './encipher/des';
5
3
  import { RSADecrypt, RSAEncrypt } from './encipher/rsa';
6
- import { createHttpSignStr, createHttpSignWithUrl } from './encipher/sign';
4
+ import { createHttpSignStr } from './encipher/sign';
7
5
  import { checkToken, generateToken } from './encipher/token';
6
+ import html from './html';
8
7
  import httpEncryption from './httpEncryption';
8
+ import {
9
+ autoSecurityWithUrl,
10
+ createEncryptedWithUrl,
11
+ createHttpSignWithUrl,
12
+ } from './urlEncryption';
9
13
  import { lxEncrypt } from './utils/encrypted';
10
14
 
15
+ export { aesKey, rsaPrivKey, rsaPublicKey, signKey } from './const';
16
+
11
17
  const security = {
12
18
  httpEncryption,
13
19
  createHttpSignStr,
14
20
  createHttpSignWithUrl,
21
+ createEncryptedWithUrl,
22
+ autoSecurityWithUrl,
15
23
  RSAEncrypt,
16
24
  RSADecrypt,
17
25
  AESEncrypt,
18
26
  AESDecrypt,
19
- DESEncrypt,
20
- DESDecrypt,
21
- KEYS,
22
27
  checkToken,
23
28
  generateToken,
24
29
  setConfig,
30
+ getConfig,
31
+ initConfig,
25
32
  lxEncrypt,
33
+ html,
26
34
  };
27
35
 
28
36
  export default security;
@@ -1,5 +1,7 @@
1
1
  import capabilities from '../clientCapabilities';
2
2
  import config from '../config';
3
+ import { commonGlobal, reqIdKey } from '../const';
4
+ import debug from '../debug';
3
5
  import { createHttpSignStr } from '../encipher/sign';
4
6
  import {
5
7
  checkIsEncryption,
@@ -10,78 +12,102 @@ import { decryptedStr, encryptedRequestParams } from '../utils/encrypted';
10
12
  import message from '../utils/message';
11
13
 
12
14
  const originRequester: any = capabilities.fetch
13
- ? (window || globalThis).fetch
15
+ ? commonGlobal.fetch
14
16
  : undefined;
15
17
 
16
18
  const fetch = (url: any, fetchOptions: any = {}): Promise<any> => {
17
19
  let requester: Promise<any>;
18
20
  const opts = { ...fetchOptions };
19
21
 
20
- // 判定是否符合忽略规则
21
- if (checkIsUrlIgnore(url, opts)) {
22
- return originRequester(url, opts);
22
+ const securityHeaderKey = config.securityHeaderKey as string;
23
+
24
+ const debugId = debug?.createId();
25
+
26
+ if (debugId) {
27
+ opts.headers[reqIdKey] = debugId;
23
28
  }
24
29
 
25
- // 请求头配置
26
- // headers.disabledSignKey 关闭签名(兼容,新模式通过将 securityHeaderKey设为false关闭)
27
- // headers.disabledEncrypted 关闭加密(兼容,新模式通过将 securityHeaderKey设为false关闭)
28
- // headers[securityHeaderKey] 指定当前服务安全模式
29
- const securityHeaderKey = config.securityHeaderKey as string;
30
- const modeInHeadParam = (opts.headers || {})[securityHeaderKey];
31
-
32
- // 优先使用当前请求头指定的安全模式
33
- const finallyMode = modeInHeadParam || config.mode;
34
-
35
- // 通过请求头参数关闭当次安全模式
36
- if (
37
- String(modeInHeadParam) === 'false' ||
38
- opts.headers?.disabledSignKey === true ||
39
- opts.headers?.disabledEncrypted === true ||
40
- finallyMode === false
41
- ) {
42
- // 不能直接删除,否则会导致disabledSignKey 这个节点删除了,第二次进来的时候导致异常
43
- // const { disabledSignKey, ...resprops } = opts?.headers;
44
- const _cloneHeader = Object.assign({}, opts.headers);
45
- delete _cloneHeader.disabledSignKey;
46
- delete _cloneHeader.disabledEncrypted;
47
- delete _cloneHeader[securityHeaderKey];
48
- requester = originRequester(url, { ...opts, headers: { ..._cloneHeader } });
49
- } else if (checkIsSignMode(finallyMode)) {
50
- // ------ 参数签名(默认) ------
51
- const signValueKeyName = config.sign?.valueKeyName as string;
52
- opts.headers = {
53
- [securityHeaderKey]: finallyMode,
54
- ...opts.headers,
55
- [signValueKeyName]: createHttpSignStr(url, fetchOptions, finallyMode),
56
- };
30
+ // 判定是否符合忽略规则
31
+ if (checkIsUrlIgnore(url, opts)) {
57
32
  requester = originRequester(url, opts);
58
- } else if (checkIsEncryption(finallyMode)) {
59
- // ------ 参数加密 ------
60
- // search和body加密
61
- const [encryptedUrl, encryptedOpts] = encryptedRequestParams(
62
- url,
63
- opts,
64
- finallyMode,
65
- );
66
-
67
- requester = originRequester(encryptedUrl, encryptedOpts);
68
33
  } else {
69
- // 其他
70
- requester = originRequester(url, opts);
34
+ // 请求头配置
35
+ // headers.disabledSignKey 关闭签名(兼容,新模式通过将 securityHeaderKey设为false关闭)
36
+ // headers.disabledEncrypted 关闭加密(兼容,新模式通过将 securityHeaderKey设为false关闭)
37
+ // headers[securityHeaderKey] 指定当前服务安全模式
38
+ const modeInHeadParam = (opts.headers || {})[securityHeaderKey];
39
+
40
+ // 优先使用当前请求头指定的安全模式
41
+ const finallyMode = modeInHeadParam || config.mode;
42
+
43
+ // 通过请求头参数关闭当次安全模式
44
+ if (
45
+ String(modeInHeadParam) === 'false' ||
46
+ opts.headers?.disabledSignKey === true ||
47
+ opts.headers?.disabledEncrypted === true ||
48
+ finallyMode === false
49
+ ) {
50
+ // 不能直接删除,否则会导致disabledSignKey 这个节点删除了,第二次进来的时候导致异常
51
+ // const { disabledSignKey, ...resprops } = opts?.headers;
52
+ const _cloneHeader = Object.assign({}, opts.headers);
53
+ delete _cloneHeader.disabledSignKey;
54
+ delete _cloneHeader.disabledEncrypted;
55
+ delete _cloneHeader[securityHeaderKey];
56
+ requester = originRequester(url, {
57
+ ...opts,
58
+ headers: { ..._cloneHeader },
59
+ });
60
+ } else if (checkIsSignMode(finallyMode)) {
61
+ // ------ 参数签名(默认) ------
62
+ const signValueKeyName = config.sign?.valueKeyName as string;
63
+ opts.headers = {
64
+ [securityHeaderKey]: finallyMode,
65
+ ...opts.headers,
66
+ [signValueKeyName]: createHttpSignStr(url, fetchOptions, finallyMode),
67
+ };
68
+ requester = originRequester(url, opts);
69
+ } else if (checkIsEncryption(finallyMode)) {
70
+ // ------ 参数加密 ------
71
+ // search和body加密
72
+ const [encryptedUrl, encryptedOpts] = encryptedRequestParams(
73
+ url,
74
+ opts,
75
+ finallyMode,
76
+ 'fetch',
77
+ );
78
+
79
+ requester = originRequester(encryptedUrl, encryptedOpts);
80
+ } else {
81
+ // 其他
82
+ requester = originRequester(url, opts);
83
+ }
84
+
85
+ // 记录请求debug信息
86
+ if (debugId) {
87
+ debug?.store(debugId, 'modeRequest', finallyMode);
88
+ debug?.store(debugId, 'url', url);
89
+ try {
90
+ debug?.store(debugId, 'param', JSON.parse(opts.body));
91
+ } catch {
92
+ debug?.store(debugId, 'param', opts.body);
93
+ }
94
+ }
71
95
  }
72
96
 
73
97
  // ------ 响应解密 ------
74
98
  return requester.then(async (response: Response) => {
99
+ // headers.get对大小写不敏感
75
100
  const securityType = response.headers.get(securityHeaderKey);
76
101
 
77
102
  if (securityType && checkIsEncryption(securityType) && response.body) {
103
+ const contentType = response.headers.get('Content-Type');
78
104
  // 响应数据
79
105
  const respResult = await response.text();
80
106
 
81
107
  // 解密数据
82
108
  let result;
83
109
  try {
84
- result = decryptedStr(respResult, securityType, config);
110
+ result = decryptedStr(respResult, securityType);
85
111
  } catch (e) {
86
112
  message.error('--------解密失败--------');
87
113
  // message.error(`请求ID:${reqId}`);
@@ -100,8 +126,14 @@ const fetch = (url: any, fetchOptions: any = {}): Promise<any> => {
100
126
  json = result;
101
127
  message.warn(`响应报文转换JSON失败, 内容为 ${result}`);
102
128
  }
129
+
130
+ // 记录响应debug信息
131
+ if (debugId) {
132
+ debug?.store(debugId, 'modeResopnse', securityType);
133
+ debug?.store(debugId, 'response', json);
134
+ }
103
135
  const blob = new Blob([JSON.stringify(json, null, 2)], {
104
- type: 'application/json',
136
+ type: contentType || 'application/json',
105
137
  });
106
138
  return new Response(blob);
107
139
  }
@@ -109,11 +141,14 @@ const fetch = (url: any, fetchOptions: any = {}): Promise<any> => {
109
141
  });
110
142
  };
111
143
 
144
+ // 标记劫持的fetch
145
+ fetch.isLxSecurity = true;
146
+
112
147
  export const conflict = () => {
113
- (window || globalThis).fetch = fetch;
148
+ commonGlobal.fetch = fetch;
114
149
  };
115
150
  export const noConflict = () => {
116
- (window || globalThis).fetch = originRequester;
151
+ commonGlobal.fetch = originRequester;
117
152
  };
118
153
 
119
154
  export default fetch;