一、DataAbility权限控制概述

在HarmonyOS 5应用开发中,DataAbility作为数据服务提供者,其权限控制机制是保障数据安全的关键环节。DataAbility的权限控制分为静态权限控制和动态权限控制两部分,通过精细化的权限管理,确保只有经过授权的Ability才能访问特定数据。

1.1 静态权限控制

静态权限控制通过在config.json配置文件中声明权限来实现,主要包含三个配置项:

  • readPermission:读取数据所需的权限
  • writePermission:写入数据所需的权限
  • Permission:通用权限(读写都需要)

当DataAbility被拉起时,系统会根据这些配置进行权限校验。如果调用方不具备相应权限,则访问会被拒绝。

1.2 动态权限控制

动态权限控制则是在运行时通过代码检查调用方权限,适用于更复杂的权限场景。开发者可以在DataAbility的业务逻辑中,动态判断调用方是否具有特定权限。

二、DataAbility权限配置实践

下面我们通过一个完整的联系人管理DataAbility示例,演示如何在HarmonyOS 5中实现DataAbility权限控制。

2.1 配置静态权限

首先在module.json5中声明DataAbility并配置权限:

{
  "module": {
    "abilities": [
      {
        "name": "ContactDataAbility",
        "type": "data",
        "uri": "dataability://com.example.contactprovider",
        "readPermission": "ohos.permission.READ_CONTACTS",
        "writePermission": "ohos.permission.WRITE_CONTACTS",
        "exported": true
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_CONTACTS",
        "reason": "读取联系人信息",
        "usedScene": {
          "abilities": ["ContactDataAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.WRITE_CONTACTS",
        "reason": "修改联系人信息",
        "usedScene": {
          "abilities": ["ContactDataAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

2.2 实现DataAbility

创建一个完整的联系人DataAbility实现类:

import dataAbility from '@ohos.data.dataAbility';
import relationalStore from '@ohos.data.relationalStore';
import featureAbility from '@ohos.ability.featureAbility';

const DB_NAME = 'contact_db';
const TABLE_NAME = 'contact';
const DB_VERSION = 1;

let rdbStore: relationalStore.RdbStore;

export default class ContactDataAbility extends dataAbility.DataAbility {
  async onInit(want) {
    console.info('ContactDataAbility onInit');
    try {
      const context = featureAbility.getContext();
      const config = {
        name: DB_NAME,
        securityLevel: relationalStore.SecurityLevel.S1
      };
      
      // 创建或打开数据库
      rdbStore = await relationalStore.getRdbStore(context, config);
      
      // 创建联系人表
      const sqlCreateTable = `CREATE TABLE IF NOT EXISTS ${TABLE_NAME} 
        (id INTEGER PRIMARY KEY AUTOINCREMENT, 
        name TEXT NOT NULL, 
        phone TEXT, 
        email TEXT)`;
      await rdbStore.executeSql(sqlCreateTable);
      
      return true;
    } catch (err) {
      console.error(`Failed to init ContactDataAbility, code is ${err.code}, message is ${err.message}`);
      return false;
    }
  }

  // 插入联系人
  async insert(uri, valueBucket) {
    console.info('ContactDataAbility insert');
    
    // 动态权限检查
    try {
      const callerTokenId = this.context.verifyCallingPermission('ohos.permission.WRITE_CONTACTS');
      if (callerTokenId < 0) {
        throw new Error('Permission denied: WRITE_CONTACTS required');
      }
    } catch (err) {
      console.error(`Permission check failed: ${err.message}`);
      throw err;
    }
    
    try {
      const insertResult = await rdbStore.insert(TABLE_NAME, valueBucket);
      return insertResult;
    } catch (err) {
      console.error(`Insert failed: ${err.message}`);
      throw err;
    }
  }

  // 查询联系人
  async query(uri, columns, predicates) {
    console.info('ContactDataAbility query');
    
    // 动态权限检查
    try {
      const callerTokenId = this.context.verifyCallingPermission('ohos.permission.READ_CONTACTS');
      if (callerTokenId < 0) {
        throw new Error('Permission denied: READ_CONTACTS required');
      }
    } catch (err) {
      console.error(`Permission check failed: ${err.message}`);
      throw err;
    }
    
    try {
      const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
      const resultSet = await rdbStore.query(predicates, columns);
      return resultSet;
    } catch (err) {
      console.error(`Query failed: ${err.message}`);
      throw err;
    }
  }

  // 更新联系人
  async update(uri, valueBucket, predicates) {
    console.info('ContactDataAbility update');
    
    // 动态权限检查
    try {
      const callerTokenId = this.context.verifyCallingPermission('ohos.permission.WRITE_CONTACTS');
      if (callerTokenId < 0) {
        throw new Error('Permission denied: WRITE_CONTACTS required');
      }
    } catch (err) {
      console.error(`Permission check failed: ${err.message}`);
      throw err;
    }
    
    try {
      const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
      const rowsUpdated = await rdbStore.update(valueBucket, predicates);
      return rowsUpdated;
    } catch (err) {
      console.error(`Update failed: ${err.message}`);
      throw err;
    }
  }

  // 删除联系人
  async delete(uri, predicates) {
    console.info('ContactDataAbility delete');
    
    // 动态权限检查
    try {
      const callerTokenId = this.context.verifyCallingPermission('ohos.permission.WRITE_CONTACTS');
      if (callerTokenId < 0) {
        throw new Error('Permission denied: WRITE_CONTACTS required');
      }
    } catch (err) {
      console.error(`Permission check failed: ${err.message}`);
      throw err;
    }
    
    try {
      const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
      const rowsDeleted = await rdbStore.delete(predicates);
      return rowsDeleted;
    } catch (err) {
      console.error(`Delete failed: ${err.message}`);
      throw err;
    }
  }
}

2.3 调用DataAbility的UI实现

创建一个调用DataAbility的UI页面,演示如何申请权限并使用DataAbility:

@Entry
@Component
struct ContactList {
  @State contacts: Array<{id: number, name: string, phone: string}> = []
  
  // 获取联系人列表
  async getContacts() {
    try {
      // 检查权限
      const permissions = ['ohos.permission.READ_CONTACTS']
      const result = await abilityAccessCtrl.requestPermissionsFromUser(permissions)
      
      if (result.authResults[0] === 0) {
        const uri = 'dataability://com.example.contactprovider/contact'
        const dataAbilityHelper = dataAbility.createDataAbilityHelper(this.context)
        const resultSet = await dataAbilityHelper.query(uri, ['id', 'name', 'phone'], null)
        
        this.contacts = []
        while (resultSet.goToNextRow()) {
          this.contacts.push({
            id: resultSet.getLong(resultSet.getColumnIndex('id')),
            name: resultSet.getString(resultSet.getColumnIndex('name')),
            phone: resultSet.getString(resultSet.getColumnIndex('phone'))
          })
        }
        resultSet.close()
      } else {
        prompt.showToast({message: '需要联系人读取权限'})
      }
    } catch (err) {
      prompt.showToast({message: `获取联系人失败: ${err.message}`})
    }
  }
  
  // 添加联系人
  async addContact() {
    try {
      // 检查权限
      const permissions = ['ohos.permission.WRITE_CONTACTS']
      const result = await abilityAccessCtrl.requestPermissionsFromUser(permissions)
      
      if (result.authResults[0] === 0) {
        const uri = 'dataability://com.example.contactprovider/contact'
        const dataAbilityHelper = dataAbility.createDataAbilityHelper(this.context)
        const values = {
          'name': '新联系人',
          'phone': '13800138000',
          'email': 'new@example.com'
        }
        await dataAbilityHelper.insert(uri, values)
        this.getContacts()
        prompt.showToast({message: '添加成功'})
      } else {
        prompt.showToast({message: '需要联系人写入权限'})
      }
    } catch (err) {
      prompt.showToast({message: `添加联系人失败: ${err.message}`})
    }
  }
  
  build() {
    Column() {
      Button('获取联系人').onClick(() => {
        this.getContacts()
      }).margin(10)
      
      Button('添加联系人').onClick(() => {
        this.addContact()
      }).margin(10)
      
      List({space: 10}) {
        ForEach(this.contacts, (item) => {
          ListItem() {
            Column() {
              Text(item.name).fontSize(20)
              Text(item.phone).fontSize(16).fontColor(Color.Gray)
            }.width('100%').padding(10)
          }
        }, item => item.id.toString())
      }.layoutWeight(1).width('100%')
    }.width('100%').height('100%')
  }
}

三、最佳实践与安全建议

  1. 最小权限原则:只申请必要的权限,并在manifest中明确说明权限用途。
  2. 动态权限检查:即使配置了静态权限,关键操作前仍应进行动态权限检查。
  3. 权限申请时机:在用户执行相关操作时申请权限,而非应用启动时一次性申请所有权限。
  4. 错误处理:妥善处理权限拒绝的情况,提供友好的用户引导。
  5. 数据加密:敏感数据应进行加密存储,即使获得数据访问权限也无法直接获取明文。

通过以上实践,开发者可以在HarmonyOS 5中构建安全可靠的DataAbility服务,确保用户数据得到充分保护。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐