OpenHarmony外設驅動使用 (五),Fingerprint_auth

OpenHarmony外設驅動使用 (五)


Fingerprint_auth

概述

功能簡介

指紋認證是端側設備不可或缺的功能,為設備提供用戶認證能力,可應用于設備解鎖、支付、應用登錄等身份認證場景。用戶注冊指紋后,指紋認證模塊就可為設備提供指紋認證的功能。指紋認證功能整體框架如圖1。

基于HDF(Hardware Driver Foundation)驅動框架開發的Fingerprint_auth驅動,能夠屏蔽硬件器件差異,為上層用戶認證框架和Fingerprint_auth服務提供穩定的指紋認證基礎能力接口,包括指紋認證執行器列表查詢、執行器信息查詢、指定指紋模板ID查詢模板信息、用戶認證框架和執行器間的指紋模板信息對賬、指紋的錄入、刪除、認證和識別等。

圖1 指紋認證功能整體框架

image

基本概念

用戶認證框架與各基礎認證服務組成的身份認證系統支持用戶認證憑據設置、刪除、認證等基礎功能。系統支持用戶身份認證,需要提供數據采集、處理、存儲及比對能力。

  • 執行器

    執行器是能夠提供以上能力的處理模塊,各基礎認證服務提供執行器能力,被身份認證框架調度完成各項基礎能力。

  • 執行器安全等級

    執行器提供能力時所在運行環境達到的安全級別。

  • 執行器角色

    • 全功能執行器:執行器可獨立處理一次憑據注冊和身份認證請求,即可提供用戶認證數據采集、處理、儲存及比對能力。

    • 采集器:執行器提供用戶認證時的數據采集能力,需要和認證器配合完成用戶認證。

    • 認證器:認證器提供用戶認證時數據處理能力,讀取存儲的憑據模板與當前認證信息完成比對。

  • 執行器類型

    同一種身份認證類型的不同認證方式會產生認證算法差異,設備器件差異也會導致算法差異,執行器根據支持的算法類型差異或對接的器件差異,會定義不同的執行器類型。

  • 用戶認證框架公鑰 & 執行器公鑰

    用戶身份認證處理需要保證用戶數據安全以及認證結果的準確性,用戶認證框架與基礎認證服務間的關鍵交互信息需要做數據完整性保護,各基礎認證服務將提供的執行器能力對接到用戶認證框架時,需要交換各自的公鑰,其中:

    1)執行器通過用戶認證框架公鑰校驗調度指令的準確性。

    2)執行器公鑰可被用戶認證框架用于校驗認證結果的準確性,同時用于執行器交互認證時的校驗交互信息的完整性。

  • 認證憑據

    在用戶設置認證憑據時認證服務會產生并存儲認證憑據,每個模板有一個ID,用于索引模板信息文件,在認證時讀取模板信息并用于與當次認證過程中產生的認證數據做對比,完成身份認證。

  • 執行器對賬

    用戶認證框架統一管理用戶身份和憑據ID的映射關系,執行器對接到用戶認證框架時,會讀取用戶身份認證框架內保存的該執行器的模板ID列表,執行器需要與自己維護的模板ID列表進行比對,并刪除冗余信息。

  • HAPs

    HAPs(Harmony Ability Packages),廣義上指可以安裝在OpenHarmony上的應用包,本章節中僅代表Fingerprint_auth驅動的上層應用。

  • IDL接口

    接口定義語言(Interface Definition Language)通過IDL編譯器編譯后,能夠生成與編程語言相關的文件:客戶端樁文件,服務器框架文件。本文主要是通過IDL接口生成的客戶端和服務端來實現Fingerprint_auth服務和驅動的通信,詳細使用方法可參考IDL簡介。

  • IPC通信

    IPC(Inter Process Communication),進程間通信是指兩個進程的數據之間產生交互,詳細原理可參考IPC通信簡介。

  • HDI

    HDI(Hardware Device Interface),硬件設備接口,位于基礎系統服務層和設備驅動層之間,是提供給硬件系統服務開發者使用的、統一的硬件設備功能抽象接口,其目的是為系統服務屏蔽底層硬件設備差異,具體可參考HDI規范。

運作機制

Fingerprint_auth驅動的主要工作是為上層用戶認證框架和Fingerprint_auth服務提供穩定的指紋認證基礎能力,保證設備上指紋認證功能可以正常運行。如圖2,Fingerprint_auth服務通過執行器列表接口獲取執行器信息后,向用戶認證框架注冊執行器。Fingerprint_auth服務通過執行器功能接口實現和Fingerprint_auth驅動的認證、識別、查詢等功能的信息交互。 開發者可基于HDF框架對不同芯片進行各自驅動的開發及HDI層接口的調用。

圖2 Fingerprint_auth服務和Fingerprint_auth驅動交互

image

約束與限制

要求設備上具有可信執行環境TEE(Trusted Execution Environment),指紋特征信息高強度加密保存在可信執行環境中。

開發指導

場景介紹

Fingerprint_auth驅動的主要工作是為上層用戶認證框架和Fingerprint_auth服務提供穩定的指紋認證基礎能力,保證設備上指紋認證功能可以正常運行。為實現上述場景的功能,開發者首先需要基于HDF驅動框架,完成Fingerprint_auth驅動開發,其次實現獲取執行器列表接口和認證、識別查詢等功能接口。

接口說明

注:以下接口列舉的為IDL接口描述生成的對應C++語言函數接口,接口聲明見idl文件(/drivers/interface/fingerprint_auth)。 在本文中,指紋憑據的錄入、認證、識別和刪除相關的HDI接口如表1所示,表2中的回調函數分別用于指紋執行器返回操作結果給框架和返回操作過程中的提示信息給上層應用。

表1 接口功能介紹

接口名稱功能介紹
GetExecutorList(std::vector<sptr<IAllInOneExecutor>>& allInOneExecutors)獲取V2_0版本執行器列表。
GetExecutorInfo(ExecutorInfo &executorInfo)獲取執行器信息,包括執行器類型、執行器角色、認證類型、安全等級、執行器公鑰等信息,用于向用戶認證框架注冊執行器。
OnRegisterFinish(const std::vector<uint64_t>& templateIdList,
const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo)
執行器注冊成功后,獲取用戶認證框架的公鑰信息;獲取用戶認證框架的模板列表用于對賬。
Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,
const sptr<IExecutorCallback>& callbackObj)
錄入指紋模板。
Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList, bool endAfterFirstFail,
const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj)
認證指紋模板(V2_0版本)。
Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,
const sptr<IExecutorCallback>& callbackObj)
識別指紋模板。
Delete(const std::vector<uint64_t>& templateIdList)刪除指紋模板。
Cancel(uint64_t scheduleId)通過scheduleId取消指定錄入、認證、識別操作。
SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo,
const sptr<IExecutorCallback>& callbackObj)
指紋認證服務向Fingerprint_auth驅動傳遞參數的通用接口。
GetProperty(const std::vector<uint64_t>& templateIdList,
const std::vector<int32_t>& propertyTypes, Property& property)
獲取執行器屬性信息。
SetCachedTemplates(const std::vector<uint64_t> &templateIdList)設置需緩存模板列表。
RegisterSaCommandCallback(const sptr<ISaCommandCallback> &callbackObj)注冊SA命令回調。

表2 回調函數介紹

接口名稱功能介紹
IExecutorCallback::OnResult(int32_t result, const std::vector<uint8_t>& extraInfo)返回操作的最終結果。
IExecutorCallback::OnTip(int32_t tip, const std::vector<uint8_t>& extraInfo)返回操作的過程交互信息。
ISaCommandCallback::OnSaCommands(const std::vector<SaCommand>& commands)發送命令列表。

開發步驟

以Hi3516DV300平臺為例,我們提供了Fingerprint_auth驅動DEMO實例,以下是目錄結構及各部分功能簡介。

// drivers/peripheral/fingerprint_auth
├── BUILD.gn     # 編譯腳本
├── bundle.json  # 組件描述文件
└── hdi_service  # Fingerprint_auth驅動實現├── BUILD.gn    # 編譯腳本├── include     # 頭文件└── src         # 源文件├── executor_impl.cpp                       # 認證、錄入等功能接口實現├── fingerprint_auth_interface_driver.cpp   # Fingerprint_auth驅動入口└── fingerprint_auth_interface_service.cpp  # 獲取執行器列表接口實現

下面結合DEMO實例介紹驅動開發的具體步驟。

  1. Fingerprint_auth驅動是基于HDF驅動框架設計,所以開發者需要按照驅動Driver Entry程序,完成Fingerprint_auth驅動框架開發,主要由Bind、Init、Release、Dispatch函數接口實現。關鍵代碼如下,詳細代碼請參見fingerprint_auth_interface_driver.cpp文件。

    // 通過自定義的HdfFingerprintAuthInterfaceHost對象包含ioService對象和真正的HDI Service實現IRemoteObject對象
    struct HdfFingerprintAuthInterfaceHost {struct IDeviceIoService ioService;OHOS::sptr<OHOS::IRemoteObject> stub;
    };// 服務接口調用響應接口
    static int32_t FingerprintAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,struct HdfSBuf *reply)
    {auto *hdfFingerprintAuthInterfaceHost = CONTAINER_OF(client->device->service, struct HdfFingerprintAuthInterfaceHost, ioService);OHOS::MessageParcel *dataParcel = nullptr;OHOS::MessageParcel *replyParcel = nullptr;OHOS::MessageOption option;if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__);return HDF_ERR_INVALID_PARAM;}if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__);return HDF_ERR_INVALID_PARAM;}return hdfFingerprintAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
    }// 初始化接口
    static int HdfFingerprintAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject)
    {HDF_LOGI("%{public}s: driver init start", __func__);return HDF_SUCCESS;
    }// Fingerprint_auth驅動對外提供的服務綁定到HDF框架
    static int HdfFingerprintAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject)
    {HDF_LOGI("%{public}s: driver bind start", __func__);auto *hdfFingerprintAuthInterfaceHost = new (std::nothrow) HdfFingerprintAuthInterfaceHost;if (hdfFingerprintAuthInterfaceHost == nullptr) {HDF_LOGE("%{public}s: failed to create create HdfFingerprintAuthInterfaceHost object", __func__);return HDF_FAILURE;}hdfFingerprintAuthInterfaceHost->ioService.Dispatch = FingerprintAuthInterfaceDriverDispatch;hdfFingerprintAuthInterfaceHost->ioService.Open = NULL;hdfFingerprintAuthInterfaceHost->ioService.Release = NULL;auto serviceImpl = OHOS::HDI::FingerprintAuth::V2_0::IFingerprintAuthInterface::Get(true);if (serviceImpl == nullptr) {HDF_LOGE("%{public}s: failed to get of implement service", __func__);delete hdfFingerprintAuthInterfaceHost;return HDF_FAILURE;}hdfFingerprintAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,OHOS::HDI::FingerprintAuth::V2_0::IFingerprintAuthInterface::GetDescriptor());if (hdfFingerprintAuthInterfaceHost->stub == nullptr) {HDF_LOGE("%{public}s: failed to get stub object", __func__);delete hdfFingerprintAuthInterfaceHost;return HDF_FAILURE;}deviceObject->service = &hdfFingerprintAuthInterfaceHost->ioService;return HDF_SUCCESS;
    }// 釋放Fingerprint_auth驅動中的資源
    static void HdfFingerprintAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject)
    {HDF_LOGI("%{public}s: driver release start", __func__);if (deviceObject->service == nullptr) {return;}auto *hdfFingerprintAuthInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfFingerprintAuthInterfaceHost, ioService);if (hdfFingerprintAuthInterfaceHost != nullptr) {delete hdfFingerprintAuthInterfaceHost;}
    }// 注冊Fingerprint_auth驅動入口數據結構體對象
    struct HdfDriverEntry g_fingerprintAuthInterfaceDriverEntry = {.moduleVersion = 1,.moduleName = "drivers_peripheral_fingerprint_auth",.Bind = HdfFingerprintAuthInterfaceDriverBind,.Init = HdfFingerprintAuthInterfaceDriverInit,.Release = HdfFingerprintAuthInterfaceDriverRelease,
    };// 調用HDF_INIT將驅動入口注冊到HDF框架中。在加載驅動時HDF框架會先調用Bind函數,再調用Init函數加載該驅動。當Init調用異常時,HDF框架會調用Release釋放驅動資源并退出
    HDF_INIT(g_fingerprintAuthInterfaceDriverEntry);
    
  2. Fingerprint_auth驅動框架開發完成后,Fingerprint_auth驅動需要向Fingerprint_auth服務和統一身份認證注冊執行器,所以需要實現獲取執行器列表接口。關鍵代碼如下,詳細代碼請參見fingerprint_auth_interface_service.cpp文件。

    // 執行器實現類
    class AllInOneExecutorImpl : public IAllInOneExecutor {
    public:AllInOneExecutorImpl(struct ExecutorInfo executorInfo);virtual ~AllInOneExecutorImpl() {}private:struct ExecutorInfo executorInfo_; // 執行器信息
    };static constexpr uint16_t SENSOR_ID = 123; // 執行器sensorID
    static constexpr uint32_t EXECUTOR_TYPE = 123; // 執行器類型
    static constexpr size_t PUBLIC_KEY_LEN = 32; // 執行器32字節公鑰// 創建HDI服務對象
    extern "C" IFingerprintAuthInterface *FingerprintAuthInterfaceImplGetInstance(void)
    {auto fingerprintAuthInterfaceService = new (std::nothrow) FingerprintAuthInterfaceService();if (fingerprintAuthInterfaceService == nullptr) {IAM_LOGE("fingerprintAuthInterfaceService is nullptr");return nullptr;}return fingerprintAuthInterfaceService;
    }// 獲取V2_0執行器列表實現
    int32_t FingerprintAuthInterfaceService::GetExecutorList(std::vector<sptr<IAllInOneExecutor>> &executorList)
    {IAM_LOGI("interface mock start");for (auto executor : executorList_) {executorList.push_back(executor);}IAM_LOGI("interface mock success");return HDF_SUCCESS;
    }
    
  3. 步驟1、2完成后基本實現了Fingerprint_auth驅動和Fingerprint_auth服務對接。接下來需實現執行器每個功能接口,來完成指紋認證基礎能力。關鍵代碼如下,詳細代碼請參見all_in_one_executor_impl.cpp文件。

    // 實現獲取執行器信息接口
    int32_t AllInOneExecutorImpl::GetExecutorInfo(ExecutorInfo &executorInfo)
    {IAM_LOGI("interface mock start");executorInfo = executorInfo_;IAM_LOGI("get executor information success");return HDF_SUCCESS;
    }// 實現執行器注冊成功后,獲取用戶認證框架的公鑰信息、獲取用戶認證框架的模板列表接口。將公鑰信息保持,模板列表用于和本地的模板做對賬
    int32_t AllInOneExecutorImpl::OnRegisterFinish(const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &frameworkPublicKey, const std::vector<uint8_t> &extraInfo)
    {IAM_LOGI("interface mock start");static_cast<void>(templateIdList);static_cast<void>(extraInfo);static_cast<void>(frameworkPublicKey);IAM_LOGI("register finish");return HDF_SUCCESS;
    }// 實現指紋錄入接口
    int32_t AllInOneExecutorImpl::Enroll(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj)
    {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS;
    }// 實現指紋認證接口
    int32_t AllInOneExecutorService::Authenticate(uint64_t scheduleId, const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj)
    {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(templateIdList);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED);int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS;
    }// 實現指紋識別接口
    int32_t AllInOneExecutorService::Identify(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj)
    {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS;
    }// 實現刪除指紋模板接口
    int32_t AllInOneExecutorService::Delete(const std::vector<uint64_t> &templateIdList)
    {IAM_LOGI("interface mock start");static_cast<void>(templateIdList);IAM_LOGI("delete success");return HDF_SUCCESS;
    }// 實現通過scheduleId取消指定操作接口
    int32_t AllInOneExecutorService::Cancel(uint64_t scheduleId)
    {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);IAM_LOGI("cancel success");return HDF_SUCCESS;
    }// 實現指紋認證服務向Fingerprint_auth驅動傳遞參數的通用接口,當前需要實現凍結與解鎖模板命令
    int32_t AllInOneExecutorService::SendCommand(int32_t commandId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj)
    {IAM_LOGI("interface mock start");static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}int32_t ret;switch (commandId) {case DriverCommandId::LOCK_TEMPLATE:IAM_LOGI("lock template, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;case DriverCommandId::UNLOCK_TEMPLATE:IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;case DriverCommandId::INIT_ALGORITHM:IAM_LOGI("init algorithm, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;default:IAM_LOGD("not support DriverCommandId : %{public}d", commandId);ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}}return HDF_SUCCESS;
    }// 實現獲取執行器屬性接口
    int32_t AllInOneExecutorService::GetProperty(const std::vector<uint64_t> &templateIdList, const std::vector<int32_t> &propertyTypes, Property &property)
    {IAM_LOGI("interface mock start");property = {};IAM_LOGI("get property success");return HDF_SUCCESS;
    }// 實現設置需緩存模板列表接口
    int32_t AllInOneExecutorService::SetCachedTemplates(const std::vector<uint64_t> &templateIdList)
    {IAM_LOGI("interface mock start");IAM_LOGI("set cached templates success");return HDF_SUCCESS;
    }// 實現注冊SA命令回調接口
    int32_t AllInOneExecutorService::RegisterSaCommandCallback(const sptr<ISaCommandCallback> &callbackObj)
    {IAM_LOGI("interface mock start");IAM_LOGI("register sa command callback success");return HDF_SUCCESS;
    }
    
  4. 用戶身份認證框架支持多driver,當增加driver或者修改driver信息,需要修改如下文件中serviceName2Config。

    // base/user_iam/fingerprint_auth/services/src/fingerprint_auth_service.cpp
    void FingerprintAuthService::StartDriverManager()
    {IAM_LOGI("start");int32_t ret = UserAuth::IDriverManager::Start(HDI_NAME_2_CONFIG);if (ret != UserAuth::ResultCode::SUCCESS) {IAM_LOGE("start driver manager failed");}
    }
    

調測驗證

驅動開發完成后,開發者可以通過用戶認證API接口開發HAP應用,基于RK3568平臺驗證。認證和取消功能驗證的測試代碼如下:

1.發起認證并獲取認證結果的測試代碼如下:

  // API version 10import type {BusinessError} from '@ohos.base';import userIAM_userAuth from '@ohos.userIAM.userAuth';// 設置認證參數const authParam: userIAM_userAuth.AuthParam = {challenge: new Uint8Array([49, 49, 49, 49, 49, 49]),authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FINGERPRINT],authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3,};// 配置認證界面const widgetParam: userIAM_userAuth.WidgetParam = {title: '請進行身份認證',};try {// 獲取認證對象let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam);console.info('get userAuth instance success');// 訂閱認證結果userAuthInstance.on('result', {onResult(result) {console.info(`userAuthInstance callback result: ${JSON.stringify(result)}`);// 可在認證結束或其他業務需要場景,取消訂閱認證結果userAuthInstance.off('result');}});console.info('auth on success');userAuthInstance.start();console.info('auth start success');} catch (error) {const err: BusinessError = error as BusinessError;console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`);}

2.取消認證的測試代碼如下:

  // API version 10import type {BusinessError} from '@ohos.base';import userIAM_userAuth from '@ohos.userIAM.userAuth';const authParam: userIAM_userAuth.AuthParam = {challenge: new Uint8Array([49, 49, 49, 49, 49, 49]),authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FINGERPRINT],authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3,};const widgetParam: userIAM_userAuth.WidgetParam = {title: '請進行身份認證',};try {// 獲取認證對象let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam);console.info('get userAuth instance success');// 開始認證userAuthInstance.start();console.info('auth start success');// 取消認證userAuthInstance.cancel();console.info('auth cancel success');} catch (error) {const err: BusinessError = error as BusinessError;console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`);}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81499.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81499.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81499.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端(vue)學習筆記(CLASS 6):路由進階

1、路由的封裝抽離 將之前寫在main.js文件中的路由配置與規則抽離出來&#xff0c;放置在router/index.js文件中&#xff0c;再將其導入回main.js文件中&#xff0c;即可實現路由的封裝抽離 例如 //index.js import { createMemoryHistory, createRouter } from vue-routerim…

前后端交互中的絕對路徑和相對路徑

前端 <form action"hello" method"post"> 1. 不加斜杠 &#xff08;相對路徑&#xff0c;如 action"hello"&#xff09; 解析規則&#xff1a;基于當前頁面的 URL 路徑部分 進行拼接。 假設當前頁面 URL 是 http://域名:端口/應用上下文…

在Odoo 18中創建進度條指南

在Odoo 18中創建進度條指南 一、創建進度條模板 首先在名為 progress_bar_widget.xml 的文件中定義一個名為 ProgressBarWidget 的新模板。該模板使用兩個CSS類&#xff1a;progress-bar-inner 用于樣式化進度條&#xff0c;progress_number 用于顯示進度百分比。您可以根據需…

Linux grep 命令詳解:常用選項、參數及實戰場景

一、grep 命令簡介 grep&#xff08;Global Regular Expression Print&#xff09;是 Linux 中用于文本搜索的核心工具&#xff0c;支持正則表達式&#xff0c;能快速定位文件中的目標內容。 二、常用選項&#xff08;Options&#xff09;及英文對照 | 選項 | 英文全稱 | 作用 …

【Java-EE進階】SpringBoot針對某個IP限流問題

目錄 簡介 1. 使用Guava的RateLimiter實現限流 添加Guava依賴 實現RateLimiter限流邏輯 限流管理類 控制器中應用限流邏輯 2. 使用計數器實現限流 限流管理類 控制器中應用限流邏輯 簡介 針對某個IP進行限流以防止惡意點擊是一種常見的反爬蟲和防止DoS的措施。限流策…

Linux問題排查-找到偷偷寫文件的進程

在 Linux 系統中&#xff0c;若要通過已修改的文件找到修改該文件的進程 PID&#xff0c;可以結合以下方法分析&#xff0c;具體取決于文件是否仍被進程打開或已被刪除但句柄仍存在&#xff1a; 一、文件仍被進程打開&#xff08;未刪除&#xff09; 如果文件當前正在被某個進…

More Effective C++:改善編程與設計(下)

目錄 條款19:了解臨時對象的來源 條款20:協助完成“返回值優化” 條款21:利用重載技術避免隱式類型轉換 條款22:考慮以操作符復合形式&#xff08;op&#xff09;取代其獨身形式&#xff08;op&#xff09; 條款23:考慮使用其他程序庫 條款24:了解virtual functions、mul…

VTK|類似CloudCompare的比例尺實現2-vtk實現

文章目錄 實現類頭文件實現類源文件調用邏輯關鍵問題縮放限制問題投影模式項目git鏈接實現類頭文件 以下是對你提供的 ScaleBarController.h 頭文件添加詳細注釋后的版本,幫助你更清晰地理解每個成員和方法的用途,尤其是在 VTK 中的作用: #ifndef SCALEBARCONTROLLER_H #de…

PostgreSQL 聯合索引生效條件

最近面試的時候&#xff0c;總會遇到一個問題 在 PostgreSQL 中&#xff0c;聯合索引在什么條件下會生效&#xff1f; 特此記錄~ 前置信息 數據庫版本 PostgreSQL 14.13, compiled by Visual C build 1941, 64-bit 建表語句 CREATE TABLE people (id SERIAL PRIMARY KEY,c…

SpringBoot項目里面發起http請求的幾種方法

在Spring Boot項目中發起HTTP請求的方法 在Spring Boot項目中&#xff0c;有幾種常用的方式可以發起HTTP請求&#xff0c;以下是主要的幾種方法&#xff1a; 1. 使用RestTemplate (Spring 5之前的主流方式) // 需要先注入RestTemplate Autowired private RestTemplate restT…

《Python星球日記》 第90天:微調的概念以及如何微調大模型?

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder??) 目錄 一、微調原理1. 什么是大模型微調?2. 為什么需要微調?3. 微調的基本流程4. 微調策略分類二、LoRA(Low-Rank Adaptation)技術詳解1. LoRA的核…

機器學習-人與機器生數據的區分模型測試 - 模型融合與檢驗

模型融合 # 先用普通Pipeline訓練 from sklearn.pipeline import Pipeline#from sklearn2pmml.pipeline import PMMLPipeline train_pipe Pipeline([(scaler, StandardScaler()),(ensemble, VotingClassifier(estimators[(rf, RandomForestClassifier(n_estimators200, max_de…

怎樣免費開發部署自己的網站?

要免費開發自己的網站&#xff0c;您可以根據自己的技術水平和需求選擇以下兩種主要方式&#xff1a; 零基礎用戶&#xff1a;建議使用如WordPress.com、Weebly、Strikingly等平臺&#xff0c;快速搭建網站。 有一定技術基礎的用戶&#xff1a;可選擇自行開發網站&#xff0c;…

調用百度云API機器翻譯

新建Python文件&#xff0c;叫 text_translator.py 輸入 import requests import jsonAPI_KEY "glYiYVF2dSc7EQ8n78VDRCpa" # 替換為自己的API Key SECRET_KEY "kUlhze8OQZ7xbVRp" # 替換為自己的Secret Keydef main():# 選擇翻譯方向while True:di…

OpenAI與微軟洽談新融資及IPO,Instagram因TikTok流失四成用戶

OpenAI與微軟洽談新融資及IPO 據悉&#xff0c;OpenAI 正與微軟洽談新融資及籌備 IPO&#xff0c;關鍵問題是微軟在 OpenAI 重組后的股權比例。微軟已投資超 130 億美元&#xff0c;雙方修訂 2019 年合同&#xff0c;微軟擬棄部分股權換新技術訪問權。OpenAI 上周放棄了有爭議轉…

git工具使用詳細教程-------命令行和TortoiseGit圖形化

下載 git下載地址&#xff1a;https://git-scm.com/downloads TortoiseGit&#xff08;圖形化工具&#xff09;下載地址&#xff1a;https://tortoisegit.org/download/ 認識git結構 工作區&#xff1a;存放代碼的地方 暫存區&#xff1a;臨時存儲&#xff0c;將工作區的代碼…

構建RAG混合開發---PythonAI+JavaEE+Vue.js前端的實踐

7GB顯存如何部署bf16精度的DeepSeek-R1 70B大模型&#xff1f;-CSDN博客 服務容錯治理框架resilience4j&sentinel基礎應用---微服務的限流/熔斷/降級解決方案-CSDN博客 conda管理python環境-CSDN博客 快速搭建對象存儲服務 - Minio&#xff0c;并解決臨時地址暴露ip、短…

【Java ee初階】jvm(3)

一、雙親委派機制&#xff08;類加載機制中&#xff0c;最經常考到的問題&#xff09; 類加載的第一個環節中&#xff0c;根據類的全限定類名&#xff08;包名類名&#xff09;找到對應的.class文件的過程。 JVM中進行類加載的操作&#xff0c;需要以來內部的模塊“類加載器”…

wps excel將表格輸出pdf時所有列在一張紙上

記錄&#xff1a;wps excel將表格輸出pdf時所有列在一張紙上 1&#xff0c;調整縮放比例&#xff0c;或選擇將所有列打印在一頁 2&#xff0c;將表格的所有鋪滿到這套虛線

分布式微服務系統架構第134集:筆記1運維服務器經驗,高并發,大數據量系統

加群聯系作者vx&#xff1a;xiaoda0423 倉庫地址&#xff1a;https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ ? 一、查看端口是否被占用的常用命令 1?? lsof 命令&…