查看: 5131|回复: 3

鸿蒙ASCF权限拒绝后openSetting引导设置与状态重置

[复制链接]
发表于 2026-9-17 13:00:00 | 显示全部楼层 |阅读模式
在鸿蒙 ASCF 开发权限相关功能时,常见现象是:用户拒绝授权后,再调用 has.authorize 不再弹窗,而是直接进入 fail。此时不能继续死等系统弹窗,需要使用设置类 API 把用户引导到元服务设置页,并在返回后重新确认权限状态。ASCF 里主要涉及两个方法:has.openSetting 用来打开元服务设置页,has.getSetting 用来查询已授权的权限列表。

一、openSetting 与 getSetting 的分工

has.openSetting 调用后拉起元服务设置页面,用户可在其中手动开启或关闭权限。调用示例:
  1. has.openSetting({
  2.   success: (res) => {
  3.     console.info('设置页返回:', res);
  4.     // res 是用户在设置页操作后的权限状态
  5.   },
  6.   fail: (err) => {
  7.     console.error('打开设置页失败:', err);
  8.   }
  9. });
复制代码

has.getSetting 查询当前已授权的权限列表:
  1. has.getSetting({
  2.   success: (res) => {
  3.     console.info('已授权权限:', res);
  4.     if (res['scope.userLocation']) {
  5.       console.info('定位权限已开启');
  6.     }
  7.     if (res['scope.camera']) {
  8.       console.info('相机权限已开启');
  9.     }
  10.   },
  11.   fail: (err) => {
  12.     console.error('查询失败:', err);
  13.   }
  14. });
复制代码

返回值是一个对象,key 是 scope 名称,value 是 boolean。只会返回明确授权或拒绝的权限,没申请过的权限不会出现在结果里。两个方法的 success 返回值格式一样,都是 { 'scope.xxx': true/false } 对象。

设置页中显示给用户的权限名称,和代码里的 scope 不一定一样。例如设置页显示“位置信息”“相机”,而代码里是 scope.userLocation、scope.camera。弹窗引导时不要对用户说“请开启 scope.camera”,应说“请开启相机权限”。

二、最容易踩的坑:openSetting 必须先 authorize

openSetting 必须在 has.authorize 之后调用。如果项目从未调用过 authorize,直接调用 openSetting 会报错:
  1. {"errMsg":"operateWXData:fail no authorize"}
复制代码

也就是说,至少先弹过一次授权弹窗,不管用户同意还是拒绝,之后才能打开设置页。正确顺序是:先 authorize(成功或失败都算),再 openSetting。

三、设置页返回后状态会重置

用户在设置页操作后,之前 authorize 的授权状态会被重置。从设置页返回后,需要重新调用 authorize 或 getSetting 获取最新状态。

同时,openSetting 的 success 回调里的权限状态有些情况下可能不是最新的。保险起见,从设置页返回后再调一次 getSetting 确认。

另外,openSetting 不能代替 authorize。它只是引导用户手动操作,用户可以在设置页选择开启或关闭,开发者无法控制。

getSetting 的另一个限制是:它只返回之前 authorize 过的 scope。从来没申请过的权限不会出现在结果里。要检查某个权限是否已授权,得先 authorize 过一次。

四、典型使用场景

场景一:授权失败后弹窗引导去设置页。这是最常见流程:authorize 失败后调用 has.showModal,用户点“去设置”再调用 has.openSetting。
  1. Page({
  2.   getLocationWithGuide() {
  3.     has.authorize({
  4.       scope: 'scope.userLocation',
  5.       success: () => {
  6.         has.getLocation({
  7.           success: (res) => {
  8.             console.info('位置:', res);
  9.           }
  10.         });
  11.       },
  12.       fail: (err) => {
  13.         console.info('授权失败,引导去设置:', err);
  14.         has.showModal({
  15.           title: '需要定位权限',
  16.           content: '请在设置中开启定位权限,否则无法使用此功能',
  17.           confirmText: '去设置',
  18.           cancelText: '取消',
  19.           success: (modalRes) => {
  20.             if (modalRes.confirm) {
  21.               has.openSetting({
  22.                 success: (settingRes) => {
  23.                   console.info('用户从设置页返回');
  24.                 }
  25.               });
  26.             }
  27.           }
  28.         });
  29.       }
  30.     });
  31.   }
  32. });
复制代码

场景二:先用 getSetting 检查,未授权再申请。
  1. Page({
  2.   checkAndAuthorize() {
  3.     has.getSetting({
  4.       success: (res) => {
  5.         if (res['scope.camera']) {
  6.           this.openCamera();
  7.         } else {
  8.           has.authorize({
  9.             scope: 'scope.camera',
  10.             success: () => {
  11.               this.openCamera();
  12.             },
  13.             fail: () => {
  14.               has.showToast({ title: '需要相机权限' });
  15.             }
  16.           });
  17.         }
  18.       }
  19.     });
  20.   },
  21.   openCamera() {
  22.     const ctx = has.createCameraContext();
  23.     // ...
  24.   }
  25. });
复制代码

场景三:页面加载时检查所有权限状态,适合设置页展示开关。
  1. Page({
  2.   data: {
  3.     permissions: {
  4.       location: false,
  5.       camera: false,
  6.       microphone: false,
  7.       contact: false
  8.     }
  9.   },
  10.   onShow() {
  11.     this.checkPermissions();
  12.   },
  13.   checkPermissions() {
  14.     has.getSetting({
  15.       success: (res) => {
  16.         this.setData({
  17.           permissions: {
  18.             location: !!res['scope.userLocation'],
  19.             camera: !!res['scope.camera'],
  20.             microphone: !!res['scope.record'],
  21.             contact: !!res['scope.contact']
  22.           }
  23.         });
  24.       }
  25.     });
  26.   },
  27.   goToSetting() {
  28.     has.openSetting();
  29.   }
  30. });
复制代码

场景四:结合辅助功能权限。项目若用 has.openAccessibility 做辅助功能检测,用户拒绝后可引导去设置。err.code === 203 表示用户手动关闭了辅助功能。
  1. has.openAccessibility({
  2.   success: () => {
  3.     console.info('辅助功能已开启');
  4.   },
  5.   fail: (err) => {
  6.     if (err.code === 203) {
  7.       has.showModal({
  8.         title: '需要辅助功能',
  9.         content: '请在设置中开启辅助功能',
  10.         confirmText: '去设置',
  11.         success: (res) => {
  12.           if (res.confirm) {
  13.             has.openSetting();
  14.           }
  15.         }
  16.       });
  17.     }
  18.   }
  19. });
复制代码

五、完整流程与封装

authorize + openSetting 的流程可以概括为:
  1. 调用 has.authorize
  2. ├── success → 权限已拿到,继续业务
  3. └── fail
  4.     ├── 弹窗提示用户
  5.     │   ├── 用户点“去设置”
  6.     │   │   └── 调用 has.openSetting
  7.     │   │       └── 用户在设置页操作
  8.     │   │           └── 返回后重新 authorize/getSetting
  9.     │   └── 用户点“取消”
  10.     │       └── 结束或降级处理
  11.     └── getSetting 检查是否还有其他权限需要申请
复制代码

由于流程固定,可以封装 ensurePermission:
  1. function ensurePermission(scope, scopeName) {
  2.   return new Promise((resolve, reject) => {
  3.     has.authorize({
  4.       scope: scope,
  5.       success: () => resolve(),
  6.       fail: () => {
  7.         has.showModal({
  8.           title: '需要' + scopeName + '权限',
  9.           content: '请在设置中开启' + scopeName + '权限',
  10.           confirmText: '去设置',
  11.           cancelText: '取消',
  12.           success: (res) => {
  13.             if (res.confirm) {
  14.               has.openSetting({
  15.                 success: () => reject(new Error('需要用户重新授权'))
  16.               });
  17.             } else {
  18.               reject(new Error('用户拒绝授权'));
  19.             }
  20.           }
  21.         });
  22.       }
  23.     });
  24.   });
  25. }
  26. async function getLocation() {
  27.   try {
  28.     await ensurePermission('scope.userLocation', '定位');
  29.     const res = await new Promise((resolve, reject) => {
  30.       has.getLocation({ success: resolve, fail: reject });
  31.     });
  32.     return res;
  33.   } catch (err) {
  34.     console.error(err.message);
  35.   }
  36. }
复制代码

六、调试与落地提醒

在 DevEco Studio 的模拟器上,openSetting 可能表现和真机不一样,建议真机测试。项目里的 openSetting Demo 已经注册到“开放能力”分类下,可以直接体验;getSetting 的示例没有单独做,但文中代码可直接复制使用。

最后记住几个关键点:openSetting 必须在 authorize 之后;设置页返回后授权状态会重置,要重新确认;getSetting 只返回申请过的权限;openSetting 回调状态可能不是最新,必要时用 getSetting 二次确认。
回复

使用道具 举报

发表于 2026-9-17 19:00:00 | 显示全部楼层

Re: 鸿蒙ASCF权限拒绝后openSetting引导设置与状态重置

感谢楼主分享,写得很清楚。之前也遇到过用户拒绝后 has.authorize 直接进 fail 不弹窗的情况,确实不能一直等系统弹窗。openSetting 之前必须先 authorize 这一点很关键,不然报 no authorize 很容易让人摸不着头脑。还有设置页返回后状态会重置,最好再调一次 getSetting 确认,这个细节也很有用。场景一里先用 showModal 说明原因再引导去设置,比直接拉设置页体验好很多。另外设置页显示名称和 scope 不一样这点也提醒得好,给用户提示时要说相机权限、位置信息权限,别直接说 scope.camera。整体流程先 authorize 再判断,失败后引导 openSetting,返回后再 getSetting 或重新 authorize,思路很顺。收藏了。
回复 支持 反对

使用道具 举报

发表于 2026-9-17 19:10:00 | 显示全部楼层

Re: 鸿蒙ASCF权限拒绝后openSetting引导设置与状态重置

这个整理很实用,几个点都踩在关键上。openSetting 前必须先 authorize,这个坑确实容易忽略,不然直接报 no authorize;从设置页回来后授权状态会重置,不能只看 openSetting 的 success,最好再 getSetting 确认一次。getSetting 只返回之前 authorize 过的 scope,所以没申请过的权限查不到,检查前先走一次 authorize 是必要前置。引导用户时用“相机权限”“位置信息”这种说法,而不是 scope.camera、scope.userLocation,体验会好很多。场景一那种 authorize 失败后 showModal 引导去 openSetting 的流程最常用,场景二先 getSetting 再 authorize 也适合按需申请。整体思路很清晰。
回复 支持 反对

使用道具 举报

发表于 2026-9-17 19:20:00 | 显示全部楼层

Re: 鸿蒙ASCF权限拒绝后openSetting引导设置与状态重置

感谢分享,这块权限流程梳理得很实用。openSetting 必须在 authorize 之后调用、提前授权过哪怕被拒才能进设置页,这个确实容易踩坑;从设置页返回后状态会重置,再调 getSetting 确认也更稳妥。弹窗引导用用户能看懂的名称,不说 scope 名称,这个细节很赞。场景二代码好像没贴完,如果方便可以补全一下,尤其是 openCamera 和授权失败后的处理,整体流程就更完整了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-7 03:10 , Processed in 0.038526 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部