如何构建自动数据脱敏的异步API拦截器,利用Proxy劫持返回值?
- 内容介绍
- 文章标签
- 相关推荐
本文共计838个文字,预计阅读时间需要4分钟。
不能直接使用 +Proxy+ 捕获异步 API(如 fetch、XMLHttpRequest)的返回值本身,因为 Promise 实例不可代理,且 resolve/reject 值是运行时动态生成的。正确的方式是:
聚焦入口劫持,而非代理返回值
异步 API 的“返回值”本质是 Promise,而 Promise.prototype 是不可写、不可配置的;Proxy 只能代理对象,无法拦截 Promise 状态变更或 .then() 内部值。因此必须前移到发起阶段:
- 重写 window.fetch:保存原函数,用新函数包裹,在 response.clone().json() 或 .text() 后对解析出的 JS 对象/字符串执行脱敏
- 打补丁 XMLHttpRequest.prototype.send:监听 onload 事件,在 this.response 可读后,根据 responseType 判断是否需 JSON 解析再脱敏
- 不劫持 Promise.then 或全局微任务队列——易引发递归、污染第三方库、破坏错误堆栈
结构化脱敏:按字段路径匹配 + 白名单驱动
自动脱敏不是简单替换字符串,而是基于响应数据结构进行精准字段识别与掩码。例如:
- 定义脱敏规则:["user.phone", "data.token", "profile.idCard"] 或正则 /.*password.*/i
- 对 JSON 响应递归遍历,若当前路径匹配规则,则将值替换为 "***" 或哈希前缀(如 "tok_abc123...")
- 保留原始数据类型(string → string,number → number),避免因脱敏导致前端解析失败
- 跳过非对象/数组响应(如纯文本、二进制 blob),只处理可结构化解析的内容类型(application/json、text/plain 含 JSON 片段)
保持 Promise 行为契约,不破坏链路
脱敏操作必须同步完成,且不能改变原始 Promise 的 resolve 值类型或抛出未捕获异常:
- 脱敏后构造新 Response 实例:new Response(JSON.stringify(sanitized), { status: res.status, headers: res.headers })
- 若原请求使用 responseType = "blob",则先转为 text/json 再脱敏,最后转回 blob(注意 Blob 构造开销)
- 所有异常需 try/catch 并 console.warn 记录,但最终仍 resolve 原始响应——避免因脱敏失败导致业务请求中断
- 不额外发请求(如上报脱敏日志走另一个 fetch),防止竞态或循环采集
上下文感知:仅对敏感环境启用脱敏
开发、测试、生产环境策略应不同,避免影响调试效率:
- 通过 window.location.hostname 或构建时注入变量判断环境,仅在预发布/生产启用脱敏
- 支持请求 header 标识(如 X-Sanitize: true)临时开启单次脱敏,便于 QA 验证
- 对带 Authorization、Cookie 的请求优先启用,静态资源请求(.js/.css/.png)跳过
本文共计838个文字,预计阅读时间需要4分钟。
不能直接使用 +Proxy+ 捕获异步 API(如 fetch、XMLHttpRequest)的返回值本身,因为 Promise 实例不可代理,且 resolve/reject 值是运行时动态生成的。正确的方式是:
聚焦入口劫持,而非代理返回值
异步 API 的“返回值”本质是 Promise,而 Promise.prototype 是不可写、不可配置的;Proxy 只能代理对象,无法拦截 Promise 状态变更或 .then() 内部值。因此必须前移到发起阶段:
- 重写 window.fetch:保存原函数,用新函数包裹,在 response.clone().json() 或 .text() 后对解析出的 JS 对象/字符串执行脱敏
- 打补丁 XMLHttpRequest.prototype.send:监听 onload 事件,在 this.response 可读后,根据 responseType 判断是否需 JSON 解析再脱敏
- 不劫持 Promise.then 或全局微任务队列——易引发递归、污染第三方库、破坏错误堆栈
结构化脱敏:按字段路径匹配 + 白名单驱动
自动脱敏不是简单替换字符串,而是基于响应数据结构进行精准字段识别与掩码。例如:
- 定义脱敏规则:["user.phone", "data.token", "profile.idCard"] 或正则 /.*password.*/i
- 对 JSON 响应递归遍历,若当前路径匹配规则,则将值替换为 "***" 或哈希前缀(如 "tok_abc123...")
- 保留原始数据类型(string → string,number → number),避免因脱敏导致前端解析失败
- 跳过非对象/数组响应(如纯文本、二进制 blob),只处理可结构化解析的内容类型(application/json、text/plain 含 JSON 片段)
保持 Promise 行为契约,不破坏链路
脱敏操作必须同步完成,且不能改变原始 Promise 的 resolve 值类型或抛出未捕获异常:
- 脱敏后构造新 Response 实例:new Response(JSON.stringify(sanitized), { status: res.status, headers: res.headers })
- 若原请求使用 responseType = "blob",则先转为 text/json 再脱敏,最后转回 blob(注意 Blob 构造开销)
- 所有异常需 try/catch 并 console.warn 记录,但最终仍 resolve 原始响应——避免因脱敏失败导致业务请求中断
- 不额外发请求(如上报脱敏日志走另一个 fetch),防止竞态或循环采集
上下文感知:仅对敏感环境启用脱敏
开发、测试、生产环境策略应不同,避免影响调试效率:
- 通过 window.location.hostname 或构建时注入变量判断环境,仅在预发布/生产启用脱敏
- 支持请求 header 标识(如 X-Sanitize: true)临时开启单次脱敏,便于 QA 验证
- 对带 Authorization、Cookie 的请求优先启用,静态资源请求(.js/.css/.png)跳过

