ArkWeb开发手记02|权限、网络白名单与页面缓存控制

搞定了 ArkWeb 基础页面搭建,把 Web 组件生命周期做了绑定,解决最头疼的内存泄漏问题。但很多同学照着代码跑通之后,马上遇到新麻烦:H5 页面调用摄像头直接拒绝、上传图片无响应;更换 H5 资源之后 APP 里面还是旧页面,缓存脏数据清不掉;部分内网域名直接拦截打不开。

这些问题大多不是代码逻辑 bug,而是 ArkWeb 的权限、网络访问策略和缓存机制没配置到位。很多新手会误以为只要申请INTERNET网络权限,所有网页就能随便跑。实际 HarmonyOS7 对 ArkWeb 做了严格的访问隔离,网页想调用设备能力、访问指定域名,都需要单独授权;浏览器缓存也需要手动管控,否则上线之后会埋下一堆隐性问题。

本篇手记,我们就把 ArkWeb 配套配置一次性讲透:网页设备权限申请、网络域名白名单配置、页面缓存策略管理,同时演示手动清理缓存、禁用缓存的实战代码。

前置阅读:掌握 ArkWeb 基础页面与生命周期管理 前置环境:DevEco Studio 白色皮肤,HarmonyOS7 SDK

一、先分清两个权限:系统权限 和 ArkWeb 网页权限

很多人在这里混淆,踩坑率极高。

  1. 应用系统权限 :在module.json5里面声明,是 APP 整体向操作系统申请权限,比如网络、相机、存储。没有这个声明,APP 根本没有调用硬件的资格。
  2. ArkWeb 网页权限 :H5 页面在 Web 内核里发起权限请求(比如 H5 调用摄像头、麦克风、相册),需要我们在 ArkTS 代码中写回调,手动同意或者拒绝网页的请求。 就算你在 module.json5 声明了相机权限,如果 ArkWeb 没有配置权限回调,H5 发起摄像头调用时,依然会直接被拦截,H5 拿不到摄像头流。

类比理解:系统权限相当于小区给你办了门禁卡,允许你进入小区;ArkWeb 权限回调相当于家里的房门,H5 访客想进房间,你需要开门放行。门禁有了,房门不开,访客依旧进不来。

1.1 module.json5 基础权限配置

首先在 module.json5 中,除了 INTERNET,把 H5 常用能力对应的系统权限提前声明。

TypeScript 复制代码
"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  },
  {
    "name": "ohos.permission.CAMERA"
  },
  {
    "name": "ohos.permission.MICROPHONE"
  },
  {
    "name": "ohos.permission.READ_MEDIA"
  }
]

注意:ohos.permission.READ_MEDIA用于 H5 选择相册图片。HarmonyOS7 不再推荐旧的 READ_EXTERNAL_STORAGE,媒体文件读取统一使用 READ_MEDIA。

1.2 ArkWeb 网页权限回调代码

Web 组件提供onPermissionRequest回调,专门拦截 H5 发起的权限申请,我们在这里做判断,决定允许或者拒绝。

TypeScript 复制代码
Web({
  src: "https://example.com",
  controller: this.webController
})
.width('100%')
.height('80%')
.onPermissionRequest((permissionReq) => {
  console.info("H5发起权限请求,类型:", permissionReq.getPermissionType())
  // 允许H5使用摄像头、麦克风
  if(permissionReq.getPermissionType() === WebPermissionType.CAMERA
    || permissionReq.getPermissionType() === WebPermissionType.MICROPHONE){
    permissionReq.grant();
  }else{
    permissionReq.deny();
  }
})
  • grant():放行,允许 H5 使用该设备能力
  • deny():拒绝本次权限申请

踩坑提醒:这个回调只处理 H5 网页发起的权限 。原生页面弹窗申请权限,还是要用requestPermissionFromUser,不要混在一起。

二、网络域名白名单:网络安全策略配置

HarmonyOS7 默认开启网络安全限制。如果访问 HTTP 非加密地址,或者访问未加入白名单的内网域名,网页直接拦截,报网络错误。很多人本地测试内网 H5,死活打不开网页,根源就是没有配置域名白名单。

网络安全配置写在module.json5的networkSecurity节点,支持配置:允许明文 HTTP 访问、域名白名单。

TypeScript 复制代码
"networkSecurity": {
  "cleartextTraffic": {
    "allow": true
  },
  "domainRules": [
    {
      "domains": ["192.168.1.*", "localhost"],
      "cleartextTraffic": {
        "allow": true
      }
    },
    {
      "domains": ["*.example.com"],
      "cleartextTraffic": {
        "allow": false
      }
    }
  ]
}

参数说明:

  1. cleartextTraffic allow:true:允许 HTTP 明文请求;生产环境公网域名建议关闭,只给内网、本地调试开启。
  2. domainRules域名规则:支持通配符*.xxx.com,可以针对不同域名单独控制是否允许 HTTP。

重要提醒:上线正式包,公网业务域名不要开启 cleartextTraffic,尽量全部使用 HTTPS,避免通信内容被抓包篡改。内网调试环境才打开 HTTP 明文访问。

常见坑

配置完白名单之后,修改 module.json5,必须卸载模拟器 / 真机上旧应用,重新编译安装。缓存的应用配置不会自动更新,很多人改完配置直接热重载,结果一直不生效,白白浪费大量调试时间。

三、ArkWeb 页面缓存机制与管控

ArkWeb 内核自带缓存,会自动保存网页资源:JS、CSS、图片、HTML。好处是二次打开页面加载更快;坏处是发布 H5 新版本之后,用户 APP 依旧加载旧缓存页面,出现版本不一致、bug 无法修复,也就是大家常说的缓存脏数据。

我们有三种缓存控制方案:

  1. 全局关闭缓存(调试阶段常用)
  2. 加载页面时禁用本次请求缓存
  3. 代码主动清理全部 Web 缓存

3.1 全局缓存设置:WebController.setCacheMode

通过控制器设置缓存模式,支持 4 种模式:

  • CacheMode.Default:默认,根据 http header 自动判断是否缓存(线上默认)
  • CacheMode.None:不使用缓存,全部从网络拉取(调试首选)
  • CacheMode.Online:优先网络,网络失败才读缓存
  • CacheMode.Offline:优先本地缓存,离线模式
TypeScript 复制代码
aboutToAppear() {
  // 调试环境,禁用缓存,每次都拉最新资源
  this.webController.setCacheMode(CacheMode.None);
}

注意:setCacheMode 建议在页面初始化 aboutToAppear 调用,不要在页面加载中途修改,中途修改容易失效。

3.2 主动清除缓存代码

产品版本更新的时候,我们可以提供一个 "清除网页缓存" 按钮,一键清理 ArkWeb 内核缓存。

TypeScript 复制代码
Button("清空Web缓存")
  .margin(10)
  .onClick(async ()=>{
    await this.webController.clearCache(true);
    // true:同时清除cookie,false只清理资源缓存
    console.info("ArkWeb缓存清理完成");
  })

clearCache(boolean includeCookie):参数 true,清理缓存同时删除网页 Cookie;false 只清理静态资源。

四、整合完整代码(权限回调 + 缓存控制 + 生命周期)

把《Web 组件 H5 加载与生命周期管理》的生命周期代码 + 权限回调 + 缓存设置整合在一起,完整可直接运行:

TypeScript 复制代码
import { Web, WebController, WebLoadStatus, WebPermissionType, CacheMode } from '@kit.ArkWeb';
import { router } from '@ohos/router';

@Entry
@Component
struct ArkWebConfigPage {
  private webController: WebController = new WebController();
  @State loadStatus: string = "待加载";
  @State hasError: boolean = false;

  aboutToAppear() {
    console.info("aboutToAppear:页面准备渲染");
    // 设置缓存模式,调试环境禁用缓存
    this.webController.setCacheMode(CacheMode.None);
  }

  onPageShow() {
    console.info("onPageShow 页面前台");
    this.webController.resume();
  }

  onPageHide() {
    console.info("onPageHide 页面后台");
    this.webController.pause();
  }

  aboutToDisappear() {
    console.info("aboutToDisappear 页面销毁");
    this.webController.destroy();
  }

  build() {
    Column() {
      Text(`H5加载状态:${this.loadStatus}`)
        .fontSize(16)
        .margin({top:10})

      Button("清空Web缓存")
        .margin(10)
        .onClick(async ()=>{
          await this.webController.clearCache(true);
        })

      if(this.hasError){
        Column(){
          Text("页面加载失败")
            .fontSize(20)
            .margin(10)
          Button("重新加载")
            .onClick(()=>{
              this.hasError = false;
              this.webController.reload();
            })
        }
      }

      Web({
        src: "https://example.com",
        controller: this.webController
      })
        .width('100%')
        .height('70%')
        .onLoadStatusChange((status: WebLoadStatus) => {
          switch(status) {
            case WebLoadStatus.StartLoading:
              this.loadStatus = "开始加载H5页面";
              this.hasError = false;
              break;
            case WebLoadStatus.FinishLoading:
              this.loadStatus = "页面加载完成";
              this.hasError = false;
              break;
            case WebLoadStatus.ErrorLoading:
              this.loadStatus = "页面加载失败,请检查网络/域名白名单";
              this.hasError = true;
              break;
          }
        })
        .onErrorReceive((err)=>{
          console.error("H5加载异常", JSON.stringify(err));
          this.hasError = true;
        })
        .onPermissionRequest((permissionReq) => {
          console.info("H5权限请求", permissionReq.getPermissionType())
          // 放行摄像头、麦克风
          if(permissionReq.getPermissionType() === WebPermissionType.CAMERA
            || permissionReq.getPermissionType() === WebPermissionType.MICROPHONE){
            permissionReq.grant();
          }else{
            permissionReq.deny();
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}

五、实战测试,验证配置效果

测试 1:H5 相机权限

写一个简单 H5 页面,调用 navigator.mediaDevices 获取摄像头。

  1. module.json5 声明 CAMERA 权限
  2. ArkWeb 增加 onPermissionRequest 放行相机权限
  3. 真机打开页面,H5 正常唤起摄像头;注释 grant 改为 deny,H5 获取媒体流直接报错。

注意:模拟器没有摄像头硬件,权限测试必须真机测试,模拟器只能做代码语法校验。

测试 2:内网域名访问

配置 networkSecurity 白名单,填写本地局域网 IP,开启 cleartextTraffic。

  • 配置正确:内网 H5 页面正常打开;
  • 不配置白名单:页面直接加载失败,报网络拦截。

测试 3:缓存验证

  1. CacheMode.Default,打开页面,修改服务端 H5 页面内容,再次访问,展示旧页面(缓存生效)
  2. 切换 CacheMode.None,刷新页面,直接拿到最新 H5 内容
  3. 点击清空缓存按钮,清理所有资源与 Cookie

这里有个坑:clearCache只能清理 ArkWeb 内核的缓存。H5 前端代码里 localStorage、sessionStorage 不属于这个缓存,不会被 clearCache 清除。如果 H5 业务数据存在 localStorage,要让前端 H5 自己清理,原生这边无法直接清除。很多同学误以为 clearCache 能清理 H5 本地存储,结果上线踩坑。

六、本篇小结

本篇我们搞定 ArkWeb 配套的三大核心配置:网页设备权限、网络域名白名单、页面缓存管理。很多 ArkWeb 线上问题,根源都在这些配置项,不是业务代码问题。

知识点回顾

  1. 区分 APP 系统权限(module.json5)和 ArkWeb 网页权限(onPermissionRequest 回调),二者缺一不可;
  2. HarmonyOS7 网络安全策略默认拦截未配置的内网域名、HTTP 明文请求,内网调试需要配置 networkSecurity 域名白名单;
  3. CacheMode 四种缓存模式,调试优先 CacheMode.None,生产环境默认 Default;
  4. webController.clearCache () 清理 Web 资源缓存 + Cookie,但无法清除 H5 localStorage/sessionStorage;
  5. 修改 module.json5 配置之后,需要卸载重装应用,热重载不会更新安全配置。

实践指南:

  1. 搭建本地简易 HTTP 服务,在内网放一个 H5 页面,配置域名白名单,在 ArkWeb 访问内网页面;
  2. 写 H5 的摄像头调用页面,真机测试 onPermissionRequest 放行 / 拒绝两种情况的效果;
  3. 分别切换 CacheMode.Default 和 CacheMode.None,观察页面资源缓存差异。
相关推荐
微软技术分享12 小时前
面向二进制安全的大模型多智能体框架
网络·安全·web安全
搁浅小泽17 小时前
新能源汽车多合一压缩机控制器CAN网络测试规范
网络·汽车
xixiaoyunya19 小时前
虚拟机备份怎么做:镜像级与文件级搭配的完整实操方案
linux·运维·网络
anew___19 小时前
《从零手写操作系统 (30):环境变量与进程上下文——export/unset与继承语义》
java·开发语言·网络·jvm·算法
拾贰_C20 小时前
【English | conversation 】call | 抖音AI短句情景:打电话---come over
数据库·redis·缓存
谢亮_vipxieliang20 小时前
Go GMP调度模型——从原理到实战的完整指南
开发语言·网络·golang
wdfk_prog21 小时前
LWIP教程 03:从 `low_level_input()` 到 `pbuf_free()`——`pbuf` 的数据视图、Chain 与引用计数
运维·网络·笔记·学习
福兮说21 小时前
IP 地址转整数的七个坑:192.168.1.1 算出负数、127.1 也是合法地址、存进 INT 直接溢出
javascript·网络·网络协议·tcp/ip·mysql·golang
xiaoye-duck21 小时前
《Linux 网络编程》深入理解 epoll(上):从接口到底层内核工作机制剖析
linux·网络