摘要 :在开发调试阶段,我们经常遇到一个诡异现象:在浏览器里运行正常的 AJAX 请求,一放到鸿蒙真机上就报错
Network Error或CORS error。这通常是因为混合应用运行在file://或resource://协议下,被浏览器的同源策略(Same-Origin Policy)无情拦截。本文讲解如何配置 ArkWeb 的跨域白名单。
🚧 1. 隐形的高墙
当前端页面位于 index.html (本地文件) 时,它的 Origin 是 file:// 或 null。
当它尝试请求后端接口 https://api.game-server.com/score 时,这就构成了一个跨域请求。
现代 WebView 的安全策略越来越严:
- 禁止
file://访问http/https(Mixed Content)。 - 禁止
file://发送 AJAX 到任何域(除非服务器显式允许Access-Control-Allow-Origin: *)。 - Preflight (OPTIONS) 请求失败。
🛠️ 2. 爆破方案
我们不需要后端配合改 Header(因为有时候调的是第三方 API),我们可以从客户端侧**"解除封印"**。
2.1 方案一:Web组件配置 (ArkTS)
ArkWeb 提供了若干个开关,用于放宽安全限制。
typescript
Web({ src: this.url, controller: this.controller })
.domStorageAccess(true)
.fileAccess(true)
.imageAccess(true)
// 关键:允许混合内容(https 页面加载 http 资源)
.mixedMode(MixedMode.All)
// 关键:允许 file 协议下的跨域访问
.fileAccess(true)
.javaScriptAccess(true)
注意:仅靠这些配置,在某些高版本 Android/鸿蒙内核上,CORS 依然会被拦截。
2.2 方案二:代理转发 (Interceptor) ✅
这是最稳妥的方案。既然 Webview 限制跨域,那我们就让 Native 代码去发请求。Native 的 HTTP 请求(http.createHttp())是不受同源策略限制的。
原理 :
Web -> 拦截特定 URL -> Native HTTP -> Server -> Native -> Web
我们复用第 8 篇中的 onInterceptRequest 机制:
typescript
// GamePlugin.ets
// 拦截 /api/ 前缀的请求
if (url.includes('/api/')) {
// 使用鸿蒙原生网络库发送请求
let httpRequest = http.createHttp();
let response = await httpRequest.request(realApiUrl, {
method: 'POST',
header: { 'Content-Type': 'application/json' },
extraData: requestBody
});
// 构造 Web 响应,并手动添加 CORS 头
let webResponse = new WebResourceResponse();
webResponse.setResponseHeader([
{ headerKey: "Access-Control-Allow-Origin", headerValue: "*" } // 强行允许
]);
webResponse.setResponseData(response.result);
return webResponse;
}
2.3 方案三:Cordova HTTP 插件
如果你不想自己写拦截器,可以直接使用社区成熟的插件 cordova-plugin-advanced-http。
它会在 JS 层替换标准的 fetch 或 XMLHttpRequest,底层通过 Native 代码发送请求。
优点:
- 自动处理 SSL Pinning。
- 无视 CORS。
- 支持 Cookie 管理。
Web 端代码:
javascript
cordova.plugin.http.post('https://api.example.com/data', {
id: 123
}, {
Authorization: 'Bearer token'
}, function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
🕸️ 3. UserAgent 伪装
有时候请求被拒不是因为跨域,而是因为服务器把来自 WebView 的请求当成了爬虫或非法客户端。
我们可以修改 UA 来伪装身份。
typescript
Web({ ... })
.userAgent("Mozilla/5.0 (Phone; HarmonyOS; GameApp/1.0) AppleWebKit/537.36")
这对于请求一些对 UA 敏感的 CDN 资源(如防盗链图片)非常有用。
📚 4. 总结
面对跨域墙:
- 初级 :配置
mixedMode和fileAccess(仅对部分旧内核有效)。 - 中级:后端开启 CORS(需要控制权)。
- 高级 :Native 代理转发。这是终极解决方案,它不仅解决了 CORS,还能解决 SSL 证书校验、自定义 DNS 解析等高级网络需求。
在混合开发中,把网络层下沉到 Native,虽然麻烦一点,但能避开 Web 安全策略的无尽骚扰。