import { RandomUtil } from './RandomUtil';
import { userAuth } from '@kit.UserAuthenticationKit';
import { BusinessError, Callback } from '@kit.BasicServicesKit';
import { LogUtil } from './LogUtil';
import { ToastUtil } from './ToastUtil';
import { StrUtil } from './StrUtil';
/**
* 认证参数类
*/
export class AuthOptions {
challenge?: Uint8Array; // 挑战值,用来防重放攻击。最大长度为32字节,可传Uint8Array([])。
authType: userAuth.UserAuthType[] = [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE, userAuth.UserAuthType.PIN]; // 认证类型列表。
authTrustLevel: userAuth.AuthTrustLevel = userAuth.AuthTrustLevel.ATL3; // 认证信任等级。
title: string = '请验证身份'; // 用户认证界面的标题,最大长度为500字符。
navigationButtonText?: string; // 导航按键的说明文本,最大长度为60字符。
showTip: boolean = false; // 是否显示提示语,默认不显示。
}
/**
* 查询认证能力是否支持的返回参数类
*/
export interface StatusResult {
status: boolean;
errorCode?: number;
errorMsg?: string;
}
/**
* 手机的生物认证(指纹、人脸、密码)工具类
* 需要权限 ohos.permission.ACCESS_BIOMETRIC
*
* author: 鸿蒙布道师
* since: 2025/04/02
*/
export class AuthUtil {
private static userAuthInstance: userAuth.UserAuthInstance | undefined;
/**
* 查询指定类型和等级的认证能力是否支持
* @param authType - 认证类型
* @param authTrustLevel - 认证信任等级
* @returns 状态结果
*/
static getAvailableStatus(authType: userAuth.UserAuthType, authTrustLevel: userAuth.AuthTrustLevel): StatusResult {
try {
userAuth.getAvailableStatus(authType, authTrustLevel);
return { status: true };
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-getAvailableStatus-异常 ~ code: ${error.code} - message: ${error.message}`);
return { status: false, errorCode: error.code, errorMsg: AuthUtil.getErrorMsg(error.code) };
}
}
/**
* 开始认证(使用默认配置)
* @param showTip - 是否显示提示语
* @param callBack - 回调函数
*/
static onStartEasy(showTip: boolean = false, callBack: Callback<userAuth.UserAuthResult>) {
const options = new AuthOptions();
options.showTip = showTip;
AuthUtil.onStart(options, callBack);
}
/**
* 开始认证(用户自定义配置)
* @param options - 认证选项
* @param callBack - 回调函数
*/
/**
* 开始认证(用户自定义配置)
* @param options - 认证选项
* @param callBack - 回调函数
*/
static onStart(options: AuthOptions, callBack: Callback<userAuth.UserAuthResult>) {
try {
AuthUtil.validateAndSetDefaults(options);
const authParam: userAuth.AuthParam = {
challenge: options.challenge!,
authType: options.authType,
authTrustLevel: options.authTrustLevel,
};
// 定义 InstanceOptions 类型
interface InstanceOptions {
title: string;
navigationButtonText?: string; // 可选字段
}
// 根据条件创建 instanceOptions
const instanceOptions: InstanceOptions = options.navigationButtonText
? { title: options.title, navigationButtonText: options.navigationButtonText }
: { title: options.title };
AuthUtil.userAuthInstance = userAuth.getUserAuthInstance(authParam, instanceOptions);
AuthUtil.userAuthInstance.on('result', {
onResult(result) {
callBack?.(result);
if (userAuth.UserAuthResultCode.SUCCESS === result.result && options.showTip) {
const errorTip = AuthUtil.getErrorMsg(result.result, '');
if (StrUtil.isNotEmpty(errorTip)) {
ToastUtil.showToast(errorTip);
}
}
},
});
AuthUtil.userAuthInstance.start(); // 开始认证
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-onStart-异常 ~ code: ${error.code} - message: ${error.message}`);
if (options.showTip) {
ToastUtil.showToast(AuthUtil.getErrorMsg(error.code, '认证失败:' + error.message));
}
}
}
/**
* 取消认证
*/
static cancel() {
try {
if (AuthUtil.userAuthInstance) {
AuthUtil.userAuthInstance.cancel();
}
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-cancel-异常 ~ code: ${error.code} - message: ${error.message}`);
}
}
/**
* 获取错误消息
* @param code - 错误码
* @param defaultMsg - 默认消息
* @returns 错误消息
*/
static getErrorMsg(code: number, defaultMsg: string = ''): string {
// 使用二维数组代替索引签名对象
const errorMessages: [number, string][] = [
[201, '权限校验失败!'],
[202, '系统API权限校验失败!'],
[401, '参数检查失败!'],
[801, '该设备不支持此API!'],
[12500001, '认证失败!'],
[12500002, '一般的操作错误!'],
[12500003, '认证被取消!'],
[12500004, '认证操作超时!'],
[12500005, '认证类型不支持!'],
[12500006, '认证信任等级不支持!'],
[12500007, '认证服务已经繁忙!'],
[12500009, '认证被锁定!'],
[12500010, '该类型的凭据没有录入!'],
[12500011, '认证被控件取消!'],
[12700001, '人脸录入过程中的操作失败!'],
];
// 查找匹配的错误码
const errorMessage = errorMessages.find((item) => item[0] === code)?.[1];
return errorMessage || defaultMsg;
}
/**
* 校验并设置默认值
* @param options - 认证选项
*/
private static validateAndSetDefaults(options: AuthOptions) {
if (!options.challenge) {
options.challenge = AuthUtil.getChallenge();
}
if (!options.authType || options.authType.length === 0) {
options.authType = [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE, userAuth.UserAuthType.PIN];
}
if (!options.authTrustLevel) {
options.authTrustLevel = userAuth.AuthTrustLevel.ATL3;
}
if (!options.title) {
options.title = '请验证身份';
}
if (options.showTip === undefined) {
options.showTip = false;
}
}
/**
* 生成挑战值
* @returns Uint8Array 类型的挑战值
*/
private static getChallenge(): Uint8Array {
// 使用传统循环生成随机数数组
const array: number[] = [];
for (let i = 0; i < 30; i++) {
array.push(RandomUtil.getRandomNumber(1, 100));
}
return new Uint8Array(array);
}
}
代码如下:
import { RandomUtil } from './RandomUtil';
import { userAuth } from '@kit.UserAuthenticationKit';
import { BusinessError, Callback } from '@kit.BasicServicesKit';
import { LogUtil } from './LogUtil';
import { ToastUtil } from './ToastUtil';
import { StrUtil } from './StrUtil';
/**
* 认证参数类
*/
export class AuthOptions {
challenge?: Uint8Array; // 挑战值,用来防重放攻击。最大长度为32字节,可传Uint8Array([])。
authType: userAuth.UserAuthType[] = [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE, userAuth.UserAuthType.PIN]; // 认证类型列表。
authTrustLevel: userAuth.AuthTrustLevel = userAuth.AuthTrustLevel.ATL3; // 认证信任等级。
title: string = '请验证身份'; // 用户认证界面的标题,最大长度为500字符。
navigationButtonText?: string; // 导航按键的说明文本,最大长度为60字符。
showTip: boolean = false; // 是否显示提示语,默认不显示。
}
/**
* 查询认证能力是否支持的返回参数类
*/
export interface StatusResult {
status: boolean;
errorCode?: number;
errorMsg?: string;
}
/**
* 手机的生物认证(指纹、人脸、密码)工具类
* 需要权限 ohos.permission.ACCESS_BIOMETRIC
*
* author: 鸿蒙布道师
* since: 2025/04/02
*/
export class AuthUtil {
private static userAuthInstance: userAuth.UserAuthInstance | undefined;
/**
* 查询指定类型和等级的认证能力是否支持
* @param authType - 认证类型
* @param authTrustLevel - 认证信任等级
* @returns 状态结果
*/
static getAvailableStatus(authType: userAuth.UserAuthType, authTrustLevel: userAuth.AuthTrustLevel): StatusResult {
try {
userAuth.getAvailableStatus(authType, authTrustLevel);
return { status: true };
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-getAvailableStatus-异常 ~ code: ${error.code} - message: ${error.message}`);
return { status: false, errorCode: error.code, errorMsg: AuthUtil.getErrorMsg(error.code) };
}
}
/**
* 开始认证(使用默认配置)
* @param showTip - 是否显示提示语
* @param callBack - 回调函数
*/
static onStartEasy(showTip: boolean = false, callBack: Callback<userAuth.UserAuthResult>) {
const options = new AuthOptions();
options.showTip = showTip;
AuthUtil.onStart(options, callBack);
}
/**
* 开始认证(用户自定义配置)
* @param options - 认证选项
* @param callBack - 回调函数
*/
/**
* 开始认证(用户自定义配置)
* @param options - 认证选项
* @param callBack - 回调函数
*/
static onStart(options: AuthOptions, callBack: Callback<userAuth.UserAuthResult>) {
try {
AuthUtil.validateAndSetDefaults(options);
const authParam: userAuth.AuthParam = {
challenge: options.challenge!,
authType: options.authType,
authTrustLevel: options.authTrustLevel,
};
// 定义 InstanceOptions 类型
interface InstanceOptions {
title: string;
navigationButtonText?: string; // 可选字段
}
// 根据条件创建 instanceOptions
const instanceOptions: InstanceOptions = options.navigationButtonText
? { title: options.title, navigationButtonText: options.navigationButtonText }
: { title: options.title };
AuthUtil.userAuthInstance = userAuth.getUserAuthInstance(authParam, instanceOptions);
AuthUtil.userAuthInstance.on('result', {
onResult(result) {
callBack?.(result);
if (userAuth.UserAuthResultCode.SUCCESS === result.result && options.showTip) {
const errorTip = AuthUtil.getErrorMsg(result.result, '');
if (StrUtil.isNotEmpty(errorTip)) {
ToastUtil.showToast(errorTip);
}
}
},
});
AuthUtil.userAuthInstance.start(); // 开始认证
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-onStart-异常 ~ code: ${error.code} - message: ${error.message}`);
if (options.showTip) {
ToastUtil.showToast(AuthUtil.getErrorMsg(error.code, '认证失败:' + error.message));
}
}
}
/**
* 取消认证
*/
static cancel() {
try {
if (AuthUtil.userAuthInstance) {
AuthUtil.userAuthInstance.cancel();
}
} catch (err) {
const error = err as BusinessError;
LogUtil.error(`AuthUtil-cancel-异常 ~ code: ${error.code} - message: ${error.message}`);
}
}
/**
* 获取错误消息
* @param code - 错误码
* @param defaultMsg - 默认消息
* @returns 错误消息
*/
static getErrorMsg(code: number, defaultMsg: string = ''): string {
// 使用二维数组代替索引签名对象
const errorMessages: [number, string][] = [
[201, '权限校验失败!'],
[202, '系统API权限校验失败!'],
[401, '参数检查失败!'],
[801, '该设备不支持此API!'],
[12500001, '认证失败!'],
[12500002, '一般的操作错误!'],
[12500003, '认证被取消!'],
[12500004, '认证操作超时!'],
[12500005, '认证类型不支持!'],
[12500006, '认证信任等级不支持!'],
[12500007, '认证服务已经繁忙!'],
[12500009, '认证被锁定!'],
[12500010, '该类型的凭据没有录入!'],
[12500011, '认证被控件取消!'],
[12700001, '人脸录入过程中的操作失败!'],
];
// 查找匹配的错误码
const errorMessage = errorMessages.find((item) => item[0] === code)?.[1];
return errorMessage || defaultMsg;
}
/**
* 校验并设置默认值
* @param options - 认证选项
*/
private static validateAndSetDefaults(options: AuthOptions) {
if (!options.challenge) {
options.challenge = AuthUtil.getChallenge();
}
if (!options.authType || options.authType.length === 0) {
options.authType = [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE, userAuth.UserAuthType.PIN];
}
if (!options.authTrustLevel) {
options.authTrustLevel = userAuth.AuthTrustLevel.ATL3;
}
if (!options.title) {
options.title = '请验证身份';
}
if (options.showTip === undefined) {
options.showTip = false;
}
}
/**
* 生成挑战值
* @returns Uint8Array 类型的挑战值
*/
private static getChallenge(): Uint8Array {
// 使用传统循环生成随机数数组
const array: number[] = [];
for (let i = 0; i < 30; i++) {
array.push(RandomUtil.getRandomNumber(1, 100));
}
return new Uint8Array(array);
}
}