@lingxiteam/ebe-utils 0.0.33 → 0.0.37
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/es/index.d.ts +5 -1
- package/es/index.js +107 -62
- package/lib/public/src/services/api/engine.ts +0 -2
- package/lib/public/src/utils/Security/clientCapabilities.ts +8 -0
- package/lib/public/src/utils/Security/config.ts +56 -49
- package/lib/public/src/utils/Security/const.ts +38 -127
- package/lib/public/src/utils/Security/debug/data.ts +86 -0
- package/lib/public/src/utils/Security/debug/index.ts +21 -0
- package/lib/public/src/utils/Security/encipher/sign.ts +25 -56
- package/lib/public/src/utils/Security/html.ts +52 -0
- package/lib/public/src/utils/Security/httpEncryption.ts +42 -23
- package/lib/public/src/utils/Security/index.ts +15 -7
- package/lib/public/src/utils/Security/requester/fetch.ts +87 -52
- package/lib/public/src/utils/Security/requester/wx.ts +60 -11
- package/lib/public/src/utils/Security/requester/xhr.ts +194 -40
- package/lib/public/src/utils/Security/types.ts +8 -90
- package/lib/public/src/utils/Security/urlEncryption.ts +108 -0
- package/lib/public/src/utils/Security/utils/atob.ts +34 -0
- package/lib/public/src/utils/Security/utils/caseInsensitive.ts +25 -0
- package/lib/public/src/utils/Security/utils/check.ts +27 -3
- package/lib/public/src/utils/Security/utils/cookie.ts +101 -53
- package/lib/public/src/utils/Security/utils/encrypted.ts +190 -43
- package/lib/public/src/utils/Security/utils/random.ts +21 -0
- package/lib/public/src/utils/Security/utils/storge.ts +22 -0
- package/lib/public/src/utils/Security/utils/url.ts +30 -5
- package/lib/public/src/utils/getSceneCode.ts +51 -0
- package/package.json +2 -2
|
@@ -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
|
-
?
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
148
|
+
commonGlobal.fetch = fetch;
|
|
114
149
|
};
|
|
115
150
|
export const noConflict = () => {
|
|
116
|
-
|
|
151
|
+
commonGlobal.fetch = originRequester;
|
|
117
152
|
};
|
|
118
153
|
|
|
119
154
|
export default fetch;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import capabilities from '../clientCapabilities';
|
|
2
2
|
import config from '../config';
|
|
3
|
+
import { reqIdKey } from '../const';
|
|
4
|
+
import debug from '../debug';
|
|
3
5
|
import { createHttpSignStr } from '../encipher/sign';
|
|
6
|
+
import { getObjectValue } from '../utils/caseInsensitive';
|
|
4
7
|
import {
|
|
5
8
|
checkIsEncryption,
|
|
6
9
|
checkIsSignMode,
|
|
@@ -8,6 +11,7 @@ import {
|
|
|
8
11
|
checkIsUrlIgnore,
|
|
9
12
|
} from '../utils/check';
|
|
10
13
|
import { decryptedStr, encryptedRequestParams } from '../utils/encrypted';
|
|
14
|
+
import { addUrlParameters } from '../utils/url';
|
|
11
15
|
|
|
12
16
|
const originRequester: any = capabilities.wxRequest ? wx.request : undefined;
|
|
13
17
|
|
|
@@ -27,6 +31,18 @@ const wxRequest = (
|
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
let requester;
|
|
34
|
+
|
|
35
|
+
// 记录原始请求数据,数据调试时查看
|
|
36
|
+
const originRequestData = opts.data;
|
|
37
|
+
|
|
38
|
+
// wx直接在控制台输入变量访问的是window下的变量,但js执行写入的变量在global,需特殊处理debugId
|
|
39
|
+
const debugId = `global.${debug?.createId()}`;
|
|
40
|
+
if (debugId) {
|
|
41
|
+
opts.header
|
|
42
|
+
? (opts.header[reqIdKey] = debugId)
|
|
43
|
+
: (opts.header = { [reqIdKey]: debugId });
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
// 请求头配置
|
|
31
47
|
// headers.disabledSignKey 关闭签名(兼容,新模式通过将 securityHeaderKey设为false关闭)
|
|
32
48
|
// headers.disabledEncrypted 关闭加密(兼容,新模式通过将 securityHeaderKey设为false关闭)
|
|
@@ -37,17 +53,22 @@ const wxRequest = (
|
|
|
37
53
|
// 优先使用当前请求头指定的安全模式
|
|
38
54
|
const finallyMode = modeInHeadParam || config.mode;
|
|
39
55
|
|
|
40
|
-
// 拦截success,处理解密
|
|
56
|
+
// 拦截success、fail,处理解密
|
|
41
57
|
const originSuccess = opts.success;
|
|
42
58
|
if (originSuccess) {
|
|
43
59
|
const success: WechatMiniprogram.RequestSuccessCallback = (response) => {
|
|
44
|
-
|
|
60
|
+
// wxrequest没有提供像 Headers.get 和 xhr.getResponseHeader 获取请求头信息(对大小写不敏感), 故此处需通过工具函数获取
|
|
61
|
+
const securityType = getObjectValue(response.header, securityHeaderKey);
|
|
45
62
|
if (checkIsEncryption(securityType)) {
|
|
46
63
|
let result;
|
|
47
64
|
try {
|
|
48
65
|
if (typeof response.data === 'string') {
|
|
49
|
-
result = decryptedStr(response.data, securityType
|
|
50
|
-
if (
|
|
66
|
+
result = decryptedStr(response.data, securityType);
|
|
67
|
+
if (
|
|
68
|
+
getObjectValue(response.header, 'Content-Type').indexOf(
|
|
69
|
+
'application/json',
|
|
70
|
+
) !== -1
|
|
71
|
+
) {
|
|
51
72
|
result = JSON.parse(result);
|
|
52
73
|
}
|
|
53
74
|
}
|
|
@@ -55,6 +76,11 @@ const wxRequest = (
|
|
|
55
76
|
response.data =
|
|
56
77
|
typeof result === 'undefined' ? response.data : result;
|
|
57
78
|
}
|
|
79
|
+
// 记录响应debug信息
|
|
80
|
+
if (debugId) {
|
|
81
|
+
debug?.store(debugId, 'modeResponse', securityType);
|
|
82
|
+
debug?.store(debugId, 'response', result);
|
|
83
|
+
}
|
|
58
84
|
}
|
|
59
85
|
originSuccess(response);
|
|
60
86
|
};
|
|
@@ -81,11 +107,23 @@ const wxRequest = (
|
|
|
81
107
|
opts.header = {
|
|
82
108
|
...(opts.header || {}),
|
|
83
109
|
[securityHeaderKey]: finallyMode,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
110
|
+
// 当get请求时,会将data拼装在url上,签名时需对url处理
|
|
111
|
+
[signValueKeyName]:
|
|
112
|
+
opts.method?.toLowerCase() === 'get' &&
|
|
113
|
+
Object.prototype.toString.call(opts.data) === '[object Object]'
|
|
114
|
+
? createHttpSignStr(
|
|
115
|
+
addUrlParameters(
|
|
116
|
+
opts.url,
|
|
117
|
+
opts.data as WechatMiniprogram.IAnyObject,
|
|
118
|
+
),
|
|
119
|
+
{ method: opts.method, body: undefined, headers: opts.header },
|
|
120
|
+
finallyMode,
|
|
121
|
+
)
|
|
122
|
+
: createHttpSignStr(
|
|
123
|
+
opts.url,
|
|
124
|
+
{ method: opts.method, body: opts.data, headers: opts.header },
|
|
125
|
+
finallyMode,
|
|
126
|
+
),
|
|
89
127
|
};
|
|
90
128
|
// 后端根据cookie里的值进行校验
|
|
91
129
|
if (checkIsSignWithSalt(finallyMode)) {
|
|
@@ -103,8 +141,12 @@ const wxRequest = (
|
|
|
103
141
|
// search和body加密
|
|
104
142
|
const [encryptedUrl, encryptedOpts] = encryptedRequestParams(
|
|
105
143
|
opts.url,
|
|
106
|
-
{
|
|
144
|
+
{
|
|
145
|
+
body: opts.data,
|
|
146
|
+
headers: { method: opts.method, ...(opts.header || {}) },
|
|
147
|
+
},
|
|
107
148
|
finallyMode,
|
|
149
|
+
'wxRequest',
|
|
108
150
|
);
|
|
109
151
|
opts.url = encryptedUrl;
|
|
110
152
|
if (encryptedOpts.body) {
|
|
@@ -113,12 +155,19 @@ const wxRequest = (
|
|
|
113
155
|
if (encryptedOpts.headers) {
|
|
114
156
|
opts.header = encryptedOpts.headers;
|
|
115
157
|
}
|
|
116
|
-
requester = originRequester(
|
|
158
|
+
requester = originRequester(opts);
|
|
117
159
|
} else {
|
|
118
160
|
// 其他
|
|
119
161
|
requester = originRequester(opts);
|
|
120
162
|
}
|
|
121
163
|
|
|
164
|
+
// 记录请求debug信息
|
|
165
|
+
if (debugId) {
|
|
166
|
+
debug?.store(debugId, 'modeRequest', finallyMode);
|
|
167
|
+
debug?.store(debugId, 'url', opts.url);
|
|
168
|
+
debug?.store(debugId, 'param', originRequestData);
|
|
169
|
+
}
|
|
170
|
+
|
|
122
171
|
return requester;
|
|
123
172
|
};
|
|
124
173
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import capabilities from '../clientCapabilities';
|
|
1
2
|
import config from '../config';
|
|
2
|
-
|
|
3
|
+
import { commonGlobal, reqIdKey } from '../const';
|
|
4
|
+
import debug from '../debug';
|
|
3
5
|
import { createHttpSignStr } from '../encipher/sign';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import {
|
|
7
|
+
checkIsEncryption,
|
|
8
|
+
checkIsSignMode,
|
|
9
|
+
checkIsUrlIgnore,
|
|
10
|
+
} from '../utils/check';
|
|
11
|
+
import { decryptedStr, encryptedRequestParams } from '../utils/encrypted';
|
|
7
12
|
|
|
8
|
-
const
|
|
13
|
+
const OriginRequester: any = capabilities.xhr ? XMLHttpRequest : undefined;
|
|
9
14
|
|
|
10
15
|
// 从请求头中获取参数
|
|
11
16
|
function getRequestHeaders(xhr: XMLHttpRequest) {
|
|
@@ -14,15 +19,16 @@ function getRequestHeaders(xhr: XMLHttpRequest) {
|
|
|
14
19
|
.getAllResponseHeaders()
|
|
15
20
|
.split('\n')
|
|
16
21
|
.forEach((header) => {
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
if (header) {
|
|
23
|
+
const [key, value] = header.split(': ');
|
|
24
|
+
headers[key] = value;
|
|
25
|
+
}
|
|
19
26
|
});
|
|
20
27
|
return headers;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
const xhr = function () {
|
|
24
|
-
|
|
25
|
-
const originXhr = new originRequester();
|
|
31
|
+
const instanceXhr = new OriginRequester();
|
|
26
32
|
|
|
27
33
|
const securityHeaderKey = config.securityHeaderKey as string;
|
|
28
34
|
const signValueKeyName = config.sign?.valueKeyName as string;
|
|
@@ -30,13 +36,16 @@ const xhr = function () {
|
|
|
30
36
|
let finallyMode: any = config.mode;
|
|
31
37
|
let xhrUrl: string = '';
|
|
32
38
|
let xhrMethod: any = '';
|
|
39
|
+
const openOptions: any = {};
|
|
33
40
|
|
|
34
41
|
// 用于存储请求头信息
|
|
35
42
|
const requestHeaders: any = {};
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const debugId = debug?.createId();
|
|
45
|
+
|
|
46
|
+
// 劫持: open 方法, 记录url、请求头等信息
|
|
47
|
+
const originalOpen = instanceXhr.open;
|
|
48
|
+
instanceXhr.open = function (
|
|
40
49
|
method: string,
|
|
41
50
|
url: string,
|
|
42
51
|
async?: boolean,
|
|
@@ -46,16 +55,26 @@ const xhr = function () {
|
|
|
46
55
|
// 记录url地址
|
|
47
56
|
xhrUrl = url;
|
|
48
57
|
xhrMethod = method;
|
|
58
|
+
openOptions.async = async;
|
|
59
|
+
openOptions.user = user;
|
|
60
|
+
openOptions.password = password;
|
|
49
61
|
// 获取请求头中的参数
|
|
50
|
-
const _requestHeaders = getRequestHeaders(
|
|
62
|
+
const _requestHeaders = getRequestHeaders(instanceXhr);
|
|
51
63
|
Object.assign(requestHeaders, _requestHeaders);
|
|
52
|
-
// 调用原始的 open
|
|
53
|
-
originalOpen.call(
|
|
64
|
+
// 调用原始的 open 方法(async不能为undefined,不然引起调用报错)
|
|
65
|
+
originalOpen.call(
|
|
66
|
+
this,
|
|
67
|
+
method,
|
|
68
|
+
url,
|
|
69
|
+
typeof async === 'undefined' ? true : async,
|
|
70
|
+
user,
|
|
71
|
+
password,
|
|
72
|
+
);
|
|
54
73
|
};
|
|
55
74
|
|
|
56
|
-
//
|
|
57
|
-
const originalSetRequestHeader =
|
|
58
|
-
|
|
75
|
+
// 劫持: setRequestHeader 方法,记录请求头信息
|
|
76
|
+
const originalSetRequestHeader = instanceXhr.setRequestHeader;
|
|
77
|
+
instanceXhr.setRequestHeader = function (
|
|
59
78
|
key: string,
|
|
60
79
|
value: string | number | boolean,
|
|
61
80
|
) {
|
|
@@ -66,9 +85,10 @@ const xhr = function () {
|
|
|
66
85
|
originalSetRequestHeader.call(this, key, value);
|
|
67
86
|
};
|
|
68
87
|
|
|
69
|
-
//
|
|
70
|
-
const originalSend =
|
|
71
|
-
|
|
88
|
+
// 劫持: send 方法,进行签名或加密处理
|
|
89
|
+
const originalSend = instanceXhr.send;
|
|
90
|
+
|
|
91
|
+
instanceXhr.send = function (data: any) {
|
|
72
92
|
// 判定是否符合忽略规则
|
|
73
93
|
if (
|
|
74
94
|
checkIsUrlIgnore(xhrUrl, {
|
|
@@ -81,41 +101,175 @@ const xhr = function () {
|
|
|
81
101
|
return;
|
|
82
102
|
}
|
|
83
103
|
|
|
84
|
-
//
|
|
85
|
-
|
|
104
|
+
// 加密后的请求数据(加密模式下保存的加密数据)
|
|
105
|
+
let encryptedData: any;
|
|
86
106
|
|
|
87
107
|
// 优先使用请求头自带的模式
|
|
88
|
-
if (
|
|
89
|
-
finallyMode =
|
|
108
|
+
if (requestHeaders[securityHeaderKey]) {
|
|
109
|
+
finallyMode = requestHeaders[securityHeaderKey];
|
|
90
110
|
}
|
|
91
111
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
// 请求头上添加安全模式标识
|
|
113
|
+
this.setRequestHeader(securityHeaderKey, finallyMode);
|
|
114
|
+
|
|
115
|
+
// 请求头上添加debug标识
|
|
116
|
+
if (debugId) {
|
|
117
|
+
this.setRequestHeader(reqIdKey, debugId);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (checkIsSignMode(finallyMode)) {
|
|
121
|
+
// 签名并添加额外的参数
|
|
122
|
+
originalSetRequestHeader.call(
|
|
123
|
+
this,
|
|
124
|
+
signValueKeyName,
|
|
125
|
+
createHttpSignStr(
|
|
126
|
+
xhrUrl,
|
|
127
|
+
{ method: xhrMethod, headers: requestHeaders, body: data },
|
|
128
|
+
finallyMode,
|
|
129
|
+
),
|
|
130
|
+
);
|
|
131
|
+
} else if (checkIsEncryption(finallyMode)) {
|
|
132
|
+
// 加密处理
|
|
133
|
+
const [encryptedUrl, encryptedOpts] = encryptedRequestParams(
|
|
98
134
|
xhrUrl,
|
|
99
|
-
{
|
|
135
|
+
{ headers: requestHeaders, body: data },
|
|
100
136
|
finallyMode,
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
'xhr',
|
|
138
|
+
);
|
|
139
|
+
// 如果url参数加密了,则重新open一个新地址
|
|
140
|
+
if (encryptedUrl !== xhrUrl) {
|
|
141
|
+
originalOpen.call(
|
|
142
|
+
this,
|
|
143
|
+
xhrMethod,
|
|
144
|
+
encryptedUrl,
|
|
145
|
+
typeof openOptions.async === 'undefined' ? true : openOptions.async,
|
|
146
|
+
openOptions.user,
|
|
147
|
+
openOptions.password,
|
|
148
|
+
);
|
|
149
|
+
// 重新设置header上的字段
|
|
150
|
+
Object.keys(requestHeaders).forEach((key) => {
|
|
151
|
+
originalSetRequestHeader.call(this, key, requestHeaders[key]);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
encryptedData = encryptedOpts.body;
|
|
155
|
+
}
|
|
103
156
|
|
|
104
|
-
//
|
|
105
|
-
|
|
157
|
+
// 记录请求debug信息
|
|
158
|
+
if (debugId) {
|
|
159
|
+
debug?.store(debugId, 'modeRequest', finallyMode);
|
|
160
|
+
debug?.store(debugId, 'url', xhrUrl);
|
|
161
|
+
debug?.store(debugId, 'param', data);
|
|
162
|
+
}
|
|
106
163
|
|
|
107
164
|
// 调用原始的 send 方法
|
|
108
|
-
originalSend.call(this, data);
|
|
165
|
+
originalSend.call(this, encryptedData || data);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// 劫持: 获取response内容,响应解密处理
|
|
169
|
+
const stashResponse: Record<string, any> = {};
|
|
170
|
+
let onreadystatechange: Function;
|
|
171
|
+
const getEncryptionResponseContent = (
|
|
172
|
+
_xhr: XMLHttpRequest,
|
|
173
|
+
key: 'response' | 'responseText' | 'responseXML',
|
|
174
|
+
securityType: any,
|
|
175
|
+
) => {
|
|
176
|
+
if (typeof stashResponse[key] === 'string') {
|
|
177
|
+
return decryptedStr(stashResponse[key], securityType);
|
|
178
|
+
}
|
|
179
|
+
return stashResponse[key];
|
|
109
180
|
};
|
|
181
|
+
instanceXhr.onreadystatechange = function () {
|
|
182
|
+
if (instanceXhr.readyState === 4) {
|
|
183
|
+
// getResponseHeader方法标头名称的搜索不区分大小写
|
|
184
|
+
const securityType = instanceXhr.getResponseHeader(securityHeaderKey);
|
|
185
|
+
// 如果是需要解密,则劫持response
|
|
186
|
+
if (securityType && checkIsEncryption(securityType)) {
|
|
187
|
+
// 记录原Response内容
|
|
188
|
+
stashResponse.response = instanceXhr.response;
|
|
189
|
+
stashResponse.responseText = instanceXhr.responseText;
|
|
190
|
+
stashResponse.responseXML = instanceXhr.responseXML;
|
|
110
191
|
|
|
111
|
-
|
|
192
|
+
// 记录响应debug信息
|
|
193
|
+
if (debugId && debug) {
|
|
194
|
+
const content = getEncryptionResponseContent(
|
|
195
|
+
instanceXhr,
|
|
196
|
+
'response',
|
|
197
|
+
securityType,
|
|
198
|
+
);
|
|
199
|
+
let finallyResponse: any;
|
|
200
|
+
try {
|
|
201
|
+
finallyResponse = JSON.parse(content);
|
|
202
|
+
} catch (e) {
|
|
203
|
+
finallyResponse = content;
|
|
204
|
+
}
|
|
205
|
+
debug.store(debugId, 'modeResponse', securityType);
|
|
206
|
+
debug.store(debugId, 'response', finallyResponse);
|
|
207
|
+
}
|
|
208
|
+
// 劫持Response getter进行解密
|
|
209
|
+
Object.defineProperties(instanceXhr, {
|
|
210
|
+
response: {
|
|
211
|
+
get() {
|
|
212
|
+
return getEncryptionResponseContent(
|
|
213
|
+
this,
|
|
214
|
+
'response',
|
|
215
|
+
securityType,
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
configurable: true,
|
|
219
|
+
enumerable: false,
|
|
220
|
+
},
|
|
221
|
+
responseText: {
|
|
222
|
+
get() {
|
|
223
|
+
return getEncryptionResponseContent(
|
|
224
|
+
this,
|
|
225
|
+
'responseText',
|
|
226
|
+
securityType,
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
configurable: true,
|
|
230
|
+
enumerable: false,
|
|
231
|
+
},
|
|
232
|
+
responseXML: {
|
|
233
|
+
get() {
|
|
234
|
+
return getEncryptionResponseContent(
|
|
235
|
+
this,
|
|
236
|
+
'responseXML',
|
|
237
|
+
securityType,
|
|
238
|
+
);
|
|
239
|
+
},
|
|
240
|
+
configurable: true,
|
|
241
|
+
enumerable: false,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (typeof onreadystatechange === 'function') {
|
|
247
|
+
onreadystatechange.call(this);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
Object.defineProperties(instanceXhr, {
|
|
252
|
+
onreadystatechange: {
|
|
253
|
+
set(value) {
|
|
254
|
+
onreadystatechange = value;
|
|
255
|
+
},
|
|
256
|
+
configurable: true,
|
|
257
|
+
enumerable: false,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// 处理完毕,返回xhr实例
|
|
262
|
+
return instanceXhr;
|
|
112
263
|
};
|
|
113
264
|
|
|
265
|
+
// 标记劫持的XMLHttpRequest
|
|
266
|
+
xhr.isLxSecurity = true;
|
|
267
|
+
|
|
114
268
|
export const conflict = () => {
|
|
115
|
-
|
|
269
|
+
commonGlobal.XMLHttpRequest = xhr;
|
|
116
270
|
};
|
|
117
271
|
export const noConflict = () => {
|
|
118
|
-
|
|
272
|
+
commonGlobal.XMLHttpRequest = OriginRequester;
|
|
119
273
|
};
|
|
120
274
|
|
|
121
275
|
export default xhr;
|