鸿蒙学习实战之路-STG系列(3/11)-用户授权管理详解

鸿蒙学习实战之路-STG系列(3/11)-用户授权管理详解

朋友们,前两篇我们介绍了 Screen Time Guard Kit 的基本概念和开发准备工作。今天这篇我们就来学习如何进行用户授权管理,包括请求用户授权、取消用户授权、以及监听用户授权状态变化 o(╯□╰)o

用户授权是使用 Screen Time Guard Kit 的前提,就像去游乐场玩要先买门票一样。没有授权,你的应用就用不了任何管控功能~

今天这篇,我会手把手带你实现用户授权管理的完整功能,全程不超过5分钟(不含你测试的时间)~


一、为什么需要用户授权?

Screen Time Guard Kit 涉及到对用户设备的时间管理和应用限制,属于非常敏感的功能。想象一下,如果随便一个应用就能控制你的手机使用时间,那岂不是太可怕了 o(╯□╰)o

所以,华为要求必须经过用户明确授权才能使用这个 Kit:

  1. 保护用户隐私 - 用户需要知道哪些应用在控制自己的设备
  2. 防止滥用 - 避免恶意应用随意控制用户设备
  3. 用户掌控 - 用户可以随时取消授权,夺回控制权

🥦 西兰花小贴士 :

用户授权就像"家长同意书",应用必须拿到用户的同意才能进行管控操作~


二、用户授权状态

用户授权有三种状态:

状态 说明 能否使用管控功能
已授权(AUTHORIZED) 用户已授权应用使用 Screen Time Guard Kit 可以使用
未授权(UNAUTHORIZED) 用户未授权应用使用 Screen Time Guard Kit 不能使用
未知(UNKNOWN) 授权状态未知,可能是首次使用 需要请求授权

🥦 西兰花警告 :

如果用户取消授权,应用将无法继续使用任何管控功能,调用相关接口会报错~


三、请求用户授权

请求用户授权是第一步,就像去游乐场要先买门票一样。

1. 业务流程

请求用户授权的流程就像这样:

复制代码
应用调用请求授权接口
    ↓
系统查询本地数据库中的授权状态
    ↓
如果已授权 → 直接返回
如果未授权 → 弹出授权对话框
    ↓
用户选择:
  - 同意授权 → 授权成功,可以使用了
  - 拒绝授权 → 抛出错误码,无法使用

2. 核心接口

请求用户授权主要涉及两个接口:

接口名 说明
requestUserAuth(context) 请求用户授权,默认策略启动后应用不可卸载
requestUserAuth(context, appConfig) 请求用户授权,可配置策略启动后应用是否可卸载
getUserAuthStatus() 获取当前授权状态

3. 开发步骤

步骤 1: 导入相关模块
typescript 复制代码
import { guardService } from '@kit.ScreenTimeGuardKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
步骤 2: 请求用户授权
typescript 复制代码
@Entry
@Component
struct AuthPage {
  @State authStatus: string = '未知';

  build() {
    Column() {
      Text('用户授权状态: ' + this.authStatus)
        .fontSize(18)
        .margin({ top: 20 })

      Button('请求用户授权')
        .onClick(async () => {
          try {
            await guardService.requestUserAuth(
              this.getUIContext().getHostContext() as common.UIAbilityContext
            );
            this.authStatus = '已授权';
            hilog.info(0x0000, 'ScreenTimeGuard', 'requestUserAuth succeeded');
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            this.authStatus = '授权失败: ' + message;
            hilog.error(0x0000, 'ScreenTimeGuard',
              `requestUserAuth failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 20 })

      Button('获取授权状态')
        .onClick(async () => {
          try {
            const status = await guardService.getUserAuthStatus();
            this.authStatus = status === guardService.AuthStatus.AUTHORIZED ? '已授权' : '未授权';
            hilog.info(0x0000, 'ScreenTimeGuard', `user auth status: ${status}`);
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'ScreenTimeGuard',
              `getUserAuthStatus failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 10 })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

🥦 西兰花小贴士 :

首次调用 requestUserAuth 时,系统会弹出授权对话框。用户同意后,再次调用就不会再弹出了~

步骤 3: 配置应用是否可卸载

如果你想配置策略启动后应用是否可卸载,可以这样:

typescript 复制代码
// 策略启动后应用不可卸载(默认)
await guardService.requestUserAuth(
  this.getUIContext().getHostContext() as common.UIAbilityContext,
  { allowUninstall: false }
);

// 策略启动后应用可卸载
await guardService.requestUserAuth(
  this.getUIContext().getHostContext() as common.UIAbilityContext,
  { allowUninstall: true }
);

🥦 西兰花警告 :

如果设置 allowUninstall: false,策略启动后应用将无法卸载,这是为了防止用户绕过管控。这个限制也合理吧? (┓( ´∀` )┏


四、取消用户授权

取消用户授权就像退票,用户可以随时取消对应用的授权。

1. 业务流程

取消用户授权的流程就像这样:

复制代码
应用调用取消授权接口
    ↓
系统查询本地数据库中的授权状态
    ↓
如果已授权 → 修改为未授权状态,返回成功
如果未授权 → 直接返回成功

2. 核心接口

取消用户授权主要涉及两个接口:

接口名 说明
revokeUserAuth() 取消用户授权
getUserAuthStatus() 获取当前授权状态

3. 开发步骤

typescript 复制代码
@Entry
@Component
struct RevokeAuthPage {
  @State authStatus: string = '未知';

  build() {
    Column() {
      Text('用户授权状态: ' + this.authStatus)
        .fontSize(18)
        .margin({ top: 20 })

      Button('取消用户授权')
        .onClick(async () => {
          try {
            await guardService.revokeUserAuth();
            this.authStatus = '未授权';
            hilog.info(0x0000, 'ScreenTimeGuard', 'revokeUserAuth succeeded');
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'ScreenTimeGuard',
              `revokeUserAuth failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 20 })

      Button('获取授权状态')
        .onClick(async () => {
          try {
            const status = await guardService.getUserAuthStatus();
            this.authStatus = status === guardService.AuthStatus.AUTHORIZED ? '已授权' : '未授权';
            hilog.info(0x0000, 'ScreenTimeGuard', `user auth status: ${status}`);
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'ScreenTimeGuard',
              `getUserAuthStatus failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 10 })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

🥦 西兰花小贴士 :

取消授权后,应用将无法继续使用任何管控功能。如果用户想再次使用,需要重新请求授权~


五、监听授权状态变化

用户不仅可以通过你的应用管理授权,还可以通过系统的"健康使用设备"页面管理授权。当用户在系统页面中开启或关闭授权时,你的应用可以通过回调监听到这个变化。

1. 业务流程

监听授权状态变化的流程就像这样:

复制代码
用户在"健康使用设备"页面中操作授权开关
    ↓
系统拉起应用的 TimeGuardExtensionAbility 进程
    ↓
执行对应的回调方法:
  - 开启授权 → onUserAuthSwitchOn
  - 关闭授权 → onUserAuthSwitchOff
    ↓
应用可以在回调中执行特定逻辑

🥦 西兰花警告 :

只有用户在"健康使用设备"页面操作授权开关时才会触发回调,应用调用 requestUserAuthrevokeUserAuth 不会触发~

2. 核心接口

监听授权状态变化主要涉及两个回调方法:

方法名 说明
onUserAuthSwitchOn() 用户开启授权时触发
onUserAuthSwitchOff() 用户关闭授权时触发

3. 开发步骤

步骤 1: 导入相关模块
typescript 复制代码
import { TimeGuardExtensionAbility } from '@kit.ScreenTimeGuardKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
步骤 2: 创建 TimeGuardExtensionAbility

创建一个继承 TimeGuardExtensionAbility 的类,重写回调方法:

typescript 复制代码
// ets/entryability/TimeGuardExtension.ets
export default class TimeGuardExtension extends TimeGuardExtensionAbility {
  async onUserAuthSwitchOn(): Promise<void> {
    hilog.info(0x0000, 'TimeGuardExtension', '用户开启了授权');
    // 在这里执行用户开启授权时的逻辑
    // 比如清除所有策略、更新UI状态等
  }

  async onUserAuthSwitchOff(): Promise<void> {
    hilog.info(0x0000, 'TimeGuardExtension', '用户关闭了授权');
    // 在这里执行用户关闭授权时的逻辑
    // 比如保存当前状态、清理资源等
  }
}
步骤 3: 配置 module.json5

entry/src/main/module.json5 文件中添加 extensionAbilities 配置:

json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    // ... 其他配置
    "extensionAbilities": [
      {
        "name": "TimeGuardExtension",
        "type": "screenTimeGuard",
        "srcEntry": "./ets/entryability/TimeGuardExtension.ets",
        "exported": false,
        "skills": [
          {
            "actions": ["action.ohos.timeGuard.listener"]
          }
        ]
      }
    ]
  }
}

🥦 西兰花小贴士 :
TimeGuardExtensionAbility 和你的应用运行在不同进程,但共用沙箱。如果需要传递数据,可以通过用户首选项、数据库或者公共事件来实现~

4. 数据传递示例

由于 TimeGuardExtensionAbility 和应用在不同进程,无法直接传递数据。这里给一个通过用户首选项传递数据的示例:

typescript 复制代码
// 应用侧 - 保存数据
import { preferences } from '@kit.ArkData';

async function saveAuthStatus(status: string) {
  let context = getContext(this) as common.UIAbilityContext;
  let pref = await preferences.getPreferences(context, 'auth_data');
  await pref.put('auth_status', status);
  await pref.flush();
}

// TimeGuardExtensionAbility 侧 - 读取数据
async function getAuthStatus(): Promise<string> {
  let pref = await preferences.getPreferences(this.context, 'auth_data');
  let status = await pref.get('auth_status', 'unknown') as string;
  return status;
}

六、完整示例代码

下面是一个完整的示例,展示了如何实现用户授权管理的完整功能:

typescript 复制代码
import { guardService } from '@kit.ScreenTimeGuardKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { preferences } from '@kit.ArkData';

@Entry
@Component
struct UserAuthDemo {
  @State authStatus: string = '未知';
  private pref: preferences.Preferences | null = null;

  async aboutToAppear() {
    // 初始化用户首选项
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    this.pref = await preferences.getPreferences(context, 'auth_data');

    // 检查当前授权状态
    await this.checkAuthStatus();
  }

  /**
   * 检查授权状态
   */
  async checkAuthStatus() {
    try {
      const status = await guardService.getUserAuthStatus();
      this.authStatus = status === guardService.AuthStatus.AUTHORIZED ? '已授权' : '未授权';
      hilog.info(0x0000, 'UserAuthDemo', `current auth status: ${status}`);

      // 保存到用户首选项
      if (this.pref) {
        await this.pref.put('auth_status', this.authStatus);
        await this.pref.flush();
      }
    } catch (err) {
      const message = (err as BusinessError).message;
      const code = (err as BusinessError).code;
      hilog.error(0x0000, 'UserAuthDemo',
        `checkAuthStatus failed with error code: ${code}, message: ${message}`);
    }
  }

  /**
   * 请求用户授权
   */
  async requestAuth() {
    try {
      await guardService.requestUserAuth(
        this.getUIContext().getHostContext() as common.UIAbilityContext
      );
      this.authStatus = '已授权';
      hilog.info(0x0000, 'UserAuthDemo', 'requestUserAuth succeeded');

      // 保存到用户首选项
      if (this.pref) {
        await this.pref.put('auth_status', this.authStatus);
        await this.pref.flush();
      }
    } catch (err) {
      const message = (err as BusinessError).message;
      const code = (err as BusinessError).code;
      this.authStatus = '授权失败: ' + message;
      hilog.error(0x0000, 'UserAuthDemo',
        `requestUserAuth failed with error code: ${code}, message: ${message}`);
    }
  }

  /**
   * 取消用户授权
   */
  async revokeAuth() {
    try {
      await guardService.revokeUserAuth();
      this.authStatus = '未授权';
      hilog.info(0x0000, 'UserAuthDemo', 'revokeUserAuth succeeded');

      // 保存到用户首选项
      if (this.pref) {
        await this.pref.put('auth_status', this.authStatus);
        await this.pref.flush();
      }
    } catch (err) {
      const message = (err as BusinessError).message;
      const code = (err as BusinessError).code;
      hilog.error(0x0000, 'UserAuthDemo',
        `revokeUserAuth failed with error code: ${code}, message: ${message}`);
    }
  }

  build() {
    Column() {
      Text('用户授权管理演示')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 40 })

      Text('当前授权状态: ' + this.authStatus)
        .fontSize(18)
        .margin({ top: 30 })
        .fontColor(
          this.authStatus === '已授权' ? '#00FF00' : 
          this.authStatus === '未授权' ? '#FF0000' : '#000000'
        )

      Button('请求用户授权')
        .onClick(() => this.requestAuth())
        .margin({ top: 30 })
        .width('80%')

      Button('取消用户授权')
        .onClick(() => this.revokeAuth())
        .margin({ top: 10 })
        .width('80%')

      Button('刷新授权状态')
        .onClick(() => this.checkAuthStatus())
        .margin({ top: 10 })
        .width('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

七、注意事项

🥦 西兰花警告:

  1. 必须先请求授权: 使用任何管控功能之前,必须先请求用户授权,否则会报错
  2. 授权对话框只弹出一次 : 首次调用 requestUserAuth 时才会弹出,之后不会再弹
  3. 取消授权后无法继续使用: 取消授权后,应用将无法继续使用任何管控功能
  4. 回调只在系统页面操作时触发: 只有用户在"健康使用设备"页面操作授权开关时才会触发回调
  5. 进程隔离 : TimeGuardExtensionAbility 和应用在不同进程,无法直接传递数据
  6. 密码保护: 如果用户设置了健康使用设备密码,取消授权时需要输入密码

八、文档资源

官方文档链接:


九、总结

用户授权管理是使用 Screen Time Guard Kit 的前提,必须先获得用户授权才能使用任何管控功能。

核心要点:

  1. 必须先请求用户授权才能使用管控功能
  2. 用户可以随时取消授权
  3. 可以通过回调监听用户在系统页面的授权状态变化
  4. TimeGuardExtensionAbility 和应用在不同进程,需要通过其他方式传递数据
  5. 取消授权后应用将无法继续使用管控功能

用户授权管理就像"买票入园",没有票就进不去游乐场 o(╯□╰)o


下一步行动

建议你:

  1. 先实现基本的请求授权和取消授权功能
  2. 添加授权状态查询功能
  3. 实现 TimeGuardExtensionAbility 监听授权状态变化
  4. 测试各种授权场景,确保功能正常
  5. 在应用启动时检查授权状态,引导用户授权

记住,不教理论,只给你能跑的代码和避坑指南! _


我是盐焗西兰花,

不教理论,只给你能跑的代码和避坑指南。

下期见!🥦

相关推荐
无巧不成书02181 小时前
Kotlin Multiplatform (KMP) 鸿蒙开发整合实战|2026最新方案
android·开发语言·kotlin·harmonyos·kmp
xiaoliuliu123452 小时前
Kylin V10 安装 zlib-devel-1.2.11-20.ky10.x86_64详细步骤
linux·运维·服务器
玩具猴_wjh2 小时前
多维度筛选 + 分页优化
笔记·学习
专业开发者2 小时前
Wi-Fi 技术学习:802.11ax BSS 着色原理与性能优化解析
网络·学习
Trouvaille ~2 小时前
【Linux】网络进阶:内网穿透、DNS与ICMP实战
linux·运维·服务器·网络·dns·nat·icmp
im_AMBER2 小时前
Leetcode 123 二叉树的层平均值 | 二叉树的右视图 | 二叉树的层序遍历
数据结构·学习·算法·leetcode·二叉树
冰西瓜6002 小时前
深度学习的数学原理(九)—— 神经网络为什么能学习特征?
深度学习·神经网络·学习
键盘鼓手苏苏2 小时前
Flutter for OpenHarmony:injector 轻量级依赖注入库(比 GetIt 更简单的选择) 深度解析与鸿蒙适配指南
css·网络·flutter·华为·rust·harmonyos
骇城迷影2 小时前
代码随想录:栈和队列篇
java·服务器·算法