1# Face_auth 2 3## 概述 4 5### 功能简介 6 7人脸识别功能是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。它是基于人的脸部特征信息进行身份识别的一种生物特征识别技术,用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别,通常也叫做人像识别、面部识别、人脸认证。人脸识别功能整体框架如图1。 8 9基于HDF(Hardware Driver Foundation)驱动框架开发的Face_auth驱动,能够屏蔽硬件器件差异,为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力接口,包括人脸识别执行器列表查询、执行器信息查询、指定人脸模板ID查询模板信息、用户认证框架和执行器间的人脸模板信息对账、人脸录入、删除、认证和识别等。 10 11**图1** 人脸识别功能整体框架 12 13![image](figures/人脸识别功能整体框架图.png "人脸识别功能整体框架图") 14 15### 基本概念 16 17用户认证框架与各基础认证服务组成的身份认证系统支持用户认证凭据设置、删除、认证等基础功能。系统支持用户身份认证,需要提供数据采集、处理、存储及比对能力。 18- 执行器 19 20 执行器是能够提供以上能力的处理模块,各基础认证服务提供执行器能力,被身份认证框架调度完成各项基础能力。 21 22- 执行器安全等级 23 24 执行器提供能力时所在运行环境达到的安全级别。 25 26- 执行器角色 27 28 - 全功能执行器:执行器可独立处理一次凭据注册和身份认证请求,即可提供用户认证数据采集、处理、储存及比对能力。 29 30 - 采集器:执行器提供用户认证时的数据采集能力,需要和认证器配合完成用户认证。 31 32 - 认证器:认证器提供用户认证时数据处理能力,读取存储的凭据模板与当前认证信息完成比对。 33 34- 执行器类型 35 36 同一种身份认证类型的不同认证方式会产生认证算法差异,设备器件差异也会导致算法差异,执行器根据支持的算法类型差异或对接的器件差异,会定义不同的执行器类型。 37 38- 用户认证框架公钥 & 执行器公钥 39 40 用户身份认证处理需要保证用户数据安全以及认证结果的准确性,用户认证框架与基础认证服务间的关键交互信息需要做数据完整性保护,各基础认证服务将提供的执行器能力对接到用户认证框架时,需要交换各自的公钥,其中: 41 42 1)执行器通过用户认证框架公钥校验调度指令的准确性。 43 44 2)执行器公钥可被用户认证框架用于校验认证结果的准确性,同时用于执行器交互认证时的校验交互信息的完整性。 45 46- 认证凭据模板 47 48 认证凭据是在用户设置认证凭据时由认证服务产生并存储,每个模板有一个ID,用于索引模板信息文件,在认证时读取模板信息并用于与当次认证过程中产生的认证数据做对比,完成身份认证。 49 50- 执行器对账 51 52 用户认证框架统一管理用户身份和凭据ID的映射关系,执行器对接到用户认证框架时,会读取用户身份认证框架内保存的该执行器的模板ID列表,执行器需要与自己维护的模板ID列表进行比对,并删除冗余信息。 53 54- HAPs 55 56 HAPs(OpenHarmony Ability Packages),广义上指可以安装在OpenHarmony上的应用包,本章节中仅代表Face_auth驱动的上层应用。 57 58- IDL接口 59 60 接口定义语言(Interface Definition Language)通过IDL编译器编译后,能够生成与编程语言相关的文件:客户端桩文件,服务器框架文件。本文主要是通过IDL接口生成的客户端和服务端来实现Face_auth服务和驱动的通信,详细使用方法可参考[IDL简介](https://gitee.com/openharmony/ability_idl_tool/blob/master/README.md)。 61 62- IPC通信 63 64 IPC(Inter Process Communication),进程间通信是指两个进程的数据之间产生交互,详细原理可参考[IPC通信简介](https://gitee.com/openharmony/communication_ipc/blob/master/README_zh.md)。 65 66- HDI 67 68 HDI(Hardware Device Interface),硬件设备接口,位于基础系统服务层和设备驱动层之间,是提供给硬件系统服务开发者使用的、统一的硬件设备功能抽象接口,其目的是为系统服务屏蔽底层硬件设备差异,具体可参考[HDI规范](../../design/hdi-design-specifications.md)。 69 70### 运作机制 71 72Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。 73开发者可基于HDF框架对不同芯片进行各自驱动的开发及HDI层接口的调用。 74 75**图2** Face_auth服务和Face_auth驱动交互 76 77![image](figures/人脸识别服务和faceauth驱动接口.png "人脸识别服务和faceauth驱动接口") 78 79### 约束与限制 80 81- 要求设备上具备摄像器件,且人脸图像像素大于100*100。 82- 要求设备上具有可信执行环境,人脸特征信息高强度加密保存在可信执行环境中。 83- 对于面部特征相似的人、面部特征不断发育的儿童,人脸特征匹配率有所不同。如果对此担忧,可考虑其他认证方式。 84 85## 开发指导 86 87### 场景介绍 88 89Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。 90 91### 接口说明 92 93注:以下接口列举的为IDL接口描述生成的对应C++语言函数接口,接口声明见idl文件(/drivers/interface/face_auth/v1_0/)。 94 95在本文中,人脸凭据的录入、认证、识别和删除相关的HDI接口如表1所示,表2中的回调函数分别用于人脸执行器返回操作结果给框架和返回操作过程中的提示信息给上层应用。 96 97**表1** 接口功能介绍 98 99| 接口名称 | 功能介绍 | 100| ----------------------------------- | ---------------------------------- | 101| GetExecutorList(std::vector<sptr<IExecutor>>& executorList) | 获取执行器列表。 | 102| GetExecutorInfo(ExecutorInfo& info) | 获取执行器信息,包括执行器类型、执行器角色、认证类型、安全等级、执行器公钥等信息,用于向用户认证框架注册执行器。 | 103| GetTemplateInfo(uint64_t templateId, TemplateInfo& info) | 获取指定人脸模板ID的模板信息。 | 104| OnRegisterFinish(const std::vector<uint64_t>& templateIdList,<br/> const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo) | 执行器注册成功后,获取用户认证框架的公钥信息;获取用户认证框架的人脸模板列表用于对账。 | 105| Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,<br/> const sptr<IExecutorCallback>& callbackObj) | 录入人脸模板。 | 106| Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList,<br/> const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 认证人脸模板。 | 107| Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,<br/> const sptr<IExecutorCallback>& callbackObj) | 识别人脸模板。 | 108| Delete(const std::vector<uint64_t>& templateIdList) | 删除人脸模板。 | 109| Cancel(uint64_t scheduleId) | 通过scheduleId取消指定录入、认证、识别操作。 | 110| SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo,<br/> const sptr<IExecutorCallback>& callbackObj) | 人脸认证服务向Face_auth驱动传递参数的通用接口。 | 111 112**表2** 回调函数介绍 113 114| 接口名称 | 功能介绍 | 115| ------------------------------------------------------------ | ------------------------ | 116| IExecutorCallback::OnResult(int32_t code, const std::vector<uint8_t>& extraInfo) | 返回操作的最终结果。 | 117| IExecutorCallback::OnTip(int32_t code, const std::vector<uint8_t>& extraInfo) | 返回操作的过程交互信息。 | 118 119### 开发步骤 120 121以Hi3516DV300平台为例,我们提供了Face_auth驱动DEMO实例,以下是目录结构及各部分功能简介。 122 123```undefined 124// drivers/peripheral/face_auth 125├── BUILD.gn # 编译脚本 126├── bundle.json # 组件描述文件 127└── hdi_service # Face_auth驱动实现 128 ├── BUILD.gn # 编译脚本 129 ├── include # 头文件 130 └── src # 源文件 131 ├── executor_impl.cpp # 认证、录入等功能接口实现 132 ├── face_auth_interface_driver.cpp # Face_auth驱动入口 133 └── face_auth_interface_service.cpp # 获取执行器列表接口实现 134``` 135 136下面结合DEMO实例介绍驱动开发的具体步骤。 137 1381. 基于HDF驱动框架,按照驱动Driver Entry程序,完成Face_auth驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现,详细代码参见[face_auth_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_driver.cpp)文件。 139 140 ```c++ 141 // 通过自定义的HdfFaceAuthInterfaceHost对象包含ioService对象和真正的HDI Service实现IRemoteObject对象 142 struct HdfFaceAuthInterfaceHost { 143 struct IDeviceIoService ioService; 144 OHOS::sptr<OHOS::IRemoteObject> stub; 145 }; 146 147 // 服务接口调用响应接口 148 static int32_t FaceAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, 149 struct HdfSBuf *reply) 150 { 151 IAM_LOGI("start"); 152 auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(client->device->service, 153 struct HdfFaceAuthInterfaceHost, ioService); 154 155 OHOS::MessageParcel *dataParcel = nullptr; 156 OHOS::MessageParcel *replyParcel = nullptr; 157 OHOS::MessageOption option; 158 159 if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { 160 IAM_LOGE("%{public}s:invalid data sbuf object to dispatch", __func__); 161 return HDF_ERR_INVALID_PARAM; 162 } 163 if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { 164 IAM_LOGE("%{public}s:invalid reply sbuf object to dispatch", __func__); 165 return HDF_ERR_INVALID_PARAM; 166 } 167 168 return hdfFaceAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); 169 } 170 171 // 初始化接口 172 int HdfFaceAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) 173 { 174 IAM_LOGI("start"); 175 if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_USERAUTH)) { 176 IAM_LOGE("set face auth hdf class failed"); 177 return HDF_FAILURE; 178 } 179 return HDF_SUCCESS; 180 } 181 182 // Face_auth驱动对外提供的服务绑定到HDF框架 183 int HdfFaceAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject) 184 { 185 IAM_LOGI("start"); 186 auto *hdfFaceAuthInterfaceHost = new (std::nothrow) HdfFaceAuthInterfaceHost; 187 if (hdfFaceAuthInterfaceHost == nullptr) { 188 IAM_LOGE("%{public}s: failed to create HdfFaceAuthInterfaceHost object", __func__); 189 return HDF_FAILURE; 190 } 191 192 hdfFaceAuthInterfaceHost->ioService.Dispatch = FaceAuthInterfaceDriverDispatch; 193 hdfFaceAuthInterfaceHost->ioService.Open = NULL; 194 hdfFaceAuthInterfaceHost->ioService.Release = NULL; 195 196 auto serviceImpl = IFaceAuthInterface::Get(true); 197 if (serviceImpl == nullptr) { 198 IAM_LOGE("%{public}s: failed to implement service", __func__); 199 return HDF_FAILURE; 200 } 201 202 hdfFaceAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, 203 IFaceAuthInterface::GetDescriptor()); 204 if (hdfFaceAuthInterfaceHost->stub == nullptr) { 205 IAM_LOGE("%{public}s: failed to get stub object", __func__); 206 return HDF_FAILURE; 207 } 208 209 deviceObject->service = &hdfFaceAuthInterfaceHost->ioService; 210 IAM_LOGI("success"); 211 return HDF_SUCCESS; 212 } 213 214 // 释放Face_auth驱动中的资源 215 void HdfFaceAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) 216 { 217 IAM_LOGI("start"); 218 auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(deviceObject->service, 219 struct HdfFaceAuthInterfaceHost, ioService); 220 delete hdfFaceAuthInterfaceHost; 221 IAM_LOGI("success"); 222 } 223 224 // 注册Face_auth驱动入口数据结构体对象 225 struct HdfDriverEntry g_faceAuthInterfaceDriverEntry = { 226 .moduleVersion = 1, 227 .moduleName = "faceauth_interface_service", 228 .Bind = HdfFaceAuthInterfaceDriverBind, 229 .Init = HdfFaceAuthInterfaceDriverInit, 230 .Release = HdfFaceAuthInterfaceDriverRelease, 231 }; 232 233 // 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 234 HDF_INIT(g_faceAuthInterfaceDriverEntry); 235 ``` 236 2372. 实现获取执行器列表接口,详细代码参见[face_auth_interface_service.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_service.cpp)文件。 238 239 ```c++ 240 // 执行器实现类 241 class ExecutorImpl : public IExecutor { 242 public: 243 ExecutorImpl(struct ExecutorInfo executorInfo); 244 virtual ~ExecutorImpl() {} 245 246 private: 247 struct ExecutorInfo executorInfo_; // 执行器信息 248 }; 249 250 static constexpr uint16_t SENSOR_ID = 123; // 执行器sensorID 251 static constexpr uint32_t EXECUTOR_TYPE = 123; // 执行器类型 252 static constexpr size_t PUBLIC_KEY_LEN = 32; // 执行器32字节公钥 253 254 // 创建HDI服务对象 255 extern "C" IFaceAuthInterface *FaceAuthInterfaceImplGetInstance(void) 256 { 257 auto faceAuthInterfaceService = new (std::nothrow) FaceAuthInterfaceService(); 258 if (faceAuthInterfaceService == nullptr) { 259 IAM_LOGE("faceAuthInterfaceService is nullptr"); 260 return nullptr; 261 } 262 return faceAuthInterfaceService; 263 } 264 265 // 获取执行器列表实现,创建执行器 266 int32_t GetExecutorList(std::vector<sptr<IExecutor>>& executorList) 267 { 268 IAM_LOGI("interface mock start"); 269 executorList.clear(); 270 struct ExecutorInfo executorInfoExample = { 271 .sensorId = SENSOR_ID, 272 .executorType = EXECUTOR_TYPE, 273 .executorRole = ExecutorRole::ALL_IN_ONE, 274 .authType = AuthType::FACE, 275 .esl = ExecutorSecureLevel::ESL0, // ExecutorSecureLevel标识执行器的安全等级,范围是ESL0~ESL3,其中ESL3标识的安全等级最高 276 .publicKey = std::vector<uint8_t>(PUBLIC_KEY_LEN, 0), // 32字节公钥,算法是Ed25519 277 .extraInfo = {}, 278 }; 279 auto executor = new (std::nothrow) ExecutorImpl(executorInfoExample); 280 if (executor == nullptr) { 281 IAM_LOGE("executor is nullptr"); 282 return HDF_FAILURE; 283 } 284 executorList.push_back(sptr<IExecutor>(executor)); 285 IAM_LOGI("interface mock success"); 286 return HDF_SUCCESS; 287 } 288 ``` 289 2903. 实现执行器每个功能接口,详细代码参见[executor_impl.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/executor_impl.cpp)文件。 291 292 ```c++ 293 // 实现获取执行器信息接口 294 int32_t GetExecutorInfo(ExecutorInfo& info) 295 { 296 IAM_LOGI("interface mock start"); 297 info = executorInfo_; 298 IAM_LOGI("get executor information success"); 299 return HDF_SUCCESS; 300 } 301 302 // 实现获取指定模板ID的模板信息接口 303 int32_t GetTemplateInfo(uint64_t templateId, TemplateInfo& info) 304 { 305 IAM_LOGI("interface mock start"); 306 static_cast<void>(templateId); 307 info = {0}; 308 IAM_LOGI("get template information success"); 309 return HDF_SUCCESS; 310 } 311 312 // 实现执行器注册成功后,获取用户认证框架的公钥信息、获取用户认证框架的模板列表接口。将公钥信息保持,模板列表用于和本地的模板做对账 313 int32_t OnRegisterFinish(const std::vector<uint64_t>& templateIdList, 314 const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo) 315 { 316 IAM_LOGI("interface mock start"); 317 static_cast<void>(templateIdList); 318 static_cast<void>(extraInfo); 319 static_cast<void>(frameworkPublicKey); 320 IAM_LOGI("register finish"); 321 return HDF_SUCCESS; 322 } 323 324 // 实现人脸录入接口 325 int32_t Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo, 326 const sptr<IExecutorCallback>& callbackObj) 327 { 328 IAM_LOGI("interface mock start"); 329 static_cast<void>(scheduleId); 330 static_cast<void>(extraInfo); 331 IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); 332 int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); 333 if (ret != ResultCode::SUCCESS) { 334 IAM_LOGE("callback result is %{public}d", ret); 335 return HDF_FAILURE; 336 } 337 return HDF_SUCCESS; 338 } 339 340 // 实现人脸认证接口 341 int32_t Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList, 342 const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) 343 { 344 IAM_LOGI("interface mock start"); 345 static_cast<void>(scheduleId); 346 static_cast<void>(templateIdList); 347 static_cast<void>(extraInfo); 348 IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED); 349 int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {}); 350 if (ret != ResultCode::SUCCESS) { 351 IAM_LOGE("callback result is %{public}d", ret); 352 return HDF_FAILURE; 353 } 354 return HDF_SUCCESS; 355 } 356 357 // 实现人脸识别接口 358 int32_t Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo, 359 const sptr<IExecutorCallback>& callbackObj) 360 { 361 IAM_LOGI("interface mock start"); 362 static_cast<void>(scheduleId); 363 static_cast<void>(extraInfo); 364 IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); 365 int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); 366 if (ret != ResultCode::SUCCESS) { 367 IAM_LOGE("callback result is %{public}d", ret); 368 return HDF_FAILURE; 369 } 370 return HDF_SUCCESS; 371 } 372 373 // 实现删除人脸模板接口 374 int32_t Delete(const std::vector<uint64_t>& templateIdList) 375 { 376 IAM_LOGI("interface mock start"); 377 static_cast<void>(templateIdList); 378 IAM_LOGI("delete success"); 379 return HDF_SUCCESS; 380 } 381 382 // 实现通过scheduleId取消指定操作接口 383 int32_t Cancel(uint64_t scheduleId) 384 { 385 IAM_LOGI("interface mock start"); 386 static_cast<void>(scheduleId); 387 IAM_LOGI("cancel success"); 388 return HDF_SUCCESS; 389 } 390 391 // 实现人脸认证服务向Face_auth驱动传递参数的通用接口,当前需要实现冻结与解锁模板命令 392 int32_t SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo, 393 const sptr<IExecutorCallback>& callbackObj) 394 { 395 IAM_LOGI("interface mock start"); 396 static_cast<void>(extraInfo); 397 int32_t ret; 398 switch (commandId) { 399 case LOCK_TEMPLATE: 400 IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS); 401 ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); 402 if (ret != ResultCode::SUCCESS) { 403 IAM_LOGE("callback result is %{public}d", ret); 404 return HDF_FAILURE; 405 } 406 break; 407 case UNLOCK_TEMPLATE: 408 IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS); 409 ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); 410 if (ret != ResultCode::SUCCESS) { 411 IAM_LOGE("callback result is %{public}d", ret); 412 return HDF_FAILURE; 413 } 414 break; 415 default: 416 IAM_LOGD("not support CommandId : %{public}d", commandId); 417 ret = callbackObj->OnResult(ResultCode::GENERAL_ERROR, {}); 418 if (ret != ResultCode::SUCCESS) { 419 IAM_LOGE("callback result is %{public}d", ret); 420 return HDF_FAILURE; 421 } 422 } 423 return HDF_SUCCESS; 424 } 425 ``` 426 4274. 用户身份认证框架支持多driver,当增加driver或者修改driver信息,需要修改如下文件中serviceName2Config。 428 429 ```c++ 430 // base/user_iam/face_auth/services/src/face_auth_service.cpp 431 void FaceAuthService::StartDriverManager() 432 { 433 IAM_LOGI("start"); 434 // 此处增加或修改driver服务名字和ID,driver服务名字和ID需要全局唯一 435 const std::map<std::string, UserAuth::ServiceConfig> serviceName2Config = { 436 {"face_auth_interface_service", {1, std::make_shared<FaceAuthDriverHdi>()}}, 437 }; 438 UserIAM::UserAuth::IDriverManager::GetInstance().Start(serviceName2Config); 439 } 440 ``` 441 442### 调测验证 443 444驱动开发完成后,通过[用户认证API接口](../../application-dev/reference/apis/js-apis-useriam-userauth.md)开发JS应用,基于Hi3516DV300平台验证。认证和取消功能验证的JS测试代码如下: 445 446 ```js 447 // API version 9 448 import userIAM_userAuth from '@ohos.userIAM.userAuth'; 449 450 let challenge = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); 451 let authType = userIAM_userAuth.UserAuthType.FACE; 452 let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1; 453 454 // 获取认证对象 455 let auth; 456 try { 457 auth = userIAM_userAuth.getAuthInstance(challenge, authType, authTrustLevel); 458 console.log("get auth instance success"); 459 } catch (error) { 460 console.log("get auth instance failed" + error); 461 } 462 463 // 订阅认证结果 464 try { 465 auth.on("result", { 466 callback: (result: userIAM_userAuth.AuthResultInfo) => { 467 console.log("authV9 result " + result.result); 468 console.log("authV9 token " + result.token); 469 console.log("authV9 remainAttempts " + result.remainAttempts); 470 console.log("authV9 lockoutDuration " + result.lockoutDuration); 471 } 472 }); 473 console.log("subscribe authentication event success"); 474 } catch (error) { 475 console.log("subscribe authentication event failed " + error); 476 } 477 478 // 开始认证 479 try { 480 auth.start(); 481 console.info("authV9 start auth success"); 482 } catch (error) { 483 console.info("authV9 start auth failed, error = " + error); 484 } 485 486 // 取消认证 487 try { 488 auth.cancel(); 489 console.info("cancel auth success"); 490 } catch (error) { 491 console.info("cancel auth failed, error = " + error); 492 } 493 494 // 取消订阅认证结果 495 try { 496 auth.off("result"); 497 console.info("cancel subscribe authentication event success"); 498 } catch (error) { 499 console.info("cancel subscribe authentication event failed, error = " + error); 500 } 501 ``` 502