完整示例截图
注意获取手机号需要企业账号才可以申请这个权限,个人帐号需要使用静默登陆获取 openid 进行登陆
前言
随着鸿蒙生态的快速发展,越来越多的开发者开始尝试将应用迁移到鸿蒙平台。作为一名 uni-app 开发者,我在将应用适配鸿蒙的过程中,遇到了用户登录这一基础但重要的需求。华为账号一键登录作为鸿蒙生态的重要开放能力,能够为用户提供便捷、安全的登录体验。但在实际集成过程中,我却经历了从迷茫到豁然开朗的曲折历程。
本文将分享我在 uni-app 项目中集成华为账号一键登录能力的完整过程,特别是如何获取用户真实手机号这个核心难题,包括走过的弯路、问题的根源、以及最终的自主实现方案。
一、需求场景与功能特点
应用场景
在开发鸿蒙应用时,用户登录是最基础也是最关键的功能。传统的登录方式存在诸多痛点:
-
短信验证码登录
- 需要用户手动输入手机号
- 等待验证码到达,体验不流畅
- 可能遇到验证码延迟或收不到的问题
-
账号密码登录
- 用户需要记忆密码
- 首次使用需要注册流程
- 密码找回流程复杂
-
第三方登录
- 需要跳转第三方应用
- 授权流程较长
- 部分用户不信任第三方授权
而华为账号一键登录,完美解决了这些痛点:
核心应用场景:
- 📱 电商应用:快速注册登录,降低用户流失率
- 🎮 游戏应用:一键登录游戏,快速进入游戏体验
- 📰 内容平台:简化登录流程,提升内容消费体验
- 💼 企业应用:安全可靠的身份认证
- 🏥 生活服务:快速获取用户手机号,便于服务通知
功能特点
anhao-login 插件提供了完整的华为账号登录能力:
✅ 获取用户唯一标识
- unionID:用户在开发者账号下的唯一标识,跨应用一致
- openID:用户在当前应用的唯一标识
- 用途:用户身份识别、账号绑定、数据关联
✅ 获取用户基础信息
- 头像:用户的华为账号头像
- 昵称:用户的华为账号昵称
- 用途:丰富用户资料,提升社交体验
✅ 快速获取手机号(核心功能)
- 匿名手机号:脱敏显示(如:131******23),无需授权
- 真实手机号:通过一键登录组件获取,需用户授权
- 用途:用户注册、身份验证、服务通知
✅ 一键授权,体验流畅
- 用户点击一次按钮即可完成授权
- 无需手动输入任何信息
- 整个流程 2-3 秒完成
✅ 安全可靠
- 基于华为账号体系,安全等级高
- 符合隐私保护规范
- 授权码单次使用,防止重放攻击
✅ 易于集成
- API 简洁友好
- 详细的文档和示例
- 完整的服务端集成说明
二、错误的尝试:走了一个月的弯路
最初的方案:使用 createAuthorizationWithHuaweiIDRequest
一个月前,我开始尝试集成华为账号一键登录功能。根据华为官方文档,我找到了账号授权的 API:createAuthorizationWithHuaweiIDRequest。
文档中提到,可以通过设置 scopes 参数来申请不同的权限。我看到有一个 phone scope,心想:"这就是获取手机号的权限!"
于是,我开始了第一次尝试:
// 错误的方案(仅适用于游戏应用)
const loginRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
loginRequest.forceAuthorization = true;
loginRequest.scopes = ["phone"];
loginRequest.permissions = ["serviceauthcode"];
loginRequest.state = util.generateRandomUUID();
loginRequest.nonce = util.generateRandomUUID();
loginRequest.idTokenSignAlgorithm = authentication.IdTokenSignAlgorithm.PS256;
const context = getContext() as common.UIAbilityContext;
const controller = new authentication.AuthenticationController(context);
controller.executeRequest(loginRequest, (error : BusinessError<Object>, data) => {
if (error) {
hilog.error(0x0000, 'HuaweiLogin', `Failed to login with profile: ${JSON.stringify(error)}`);
return;
}
});
} catch (error) {
hilog.error(0x0000, 'HuaweiLogin', `Exception in hmLoginWithProfile: ${error}`);
}
持续一个月的"没有权限"错误
但是,无论我如何尝试,调用这个 API 时总是返回"没有权限"的错误。我反复检查了所有配置:
✅ AppGallery Connect 配置
- 应用已正确创建
- 应用信息已完善
- SHA256 指纹已正确配置
✅ 开放能力申请
- 已申请"华为账号一键登录"能力
- 审核状态:已通过
✅ 客户端配置
client_id已正确配置到module.json5- 应用签名与平台配置一致
- 代码中的 API 调用看起来没问题
✅ 权限配置
manifest.json中已添加必要权限- 鸿蒙应用权限已正确声明
所有配置看起来都没有问题,但就是无法获取手机号权限。这个问题困扰了我整整一个月。
尝试过的各种方法
在这一个月里,我尝试了各种可能的解决方案:
- 重新申请开放能力:以为是审核有问题,重新申请了好几次
- 更换测试设备:换了不同的鸿蒙设备测试
- 重新生成签名:以为是签名配置问题
- 查阅官方文档:把账号服务相关文档翻了好几遍
- 搜索开发者论坛:看了很多类似问题的讨论
- 参考其他项目:找了一些开源项目的代码参考
但是,所有的尝试都以失败告终。我开始怀疑:是不是华为的这个 API 在 uni-app 环境下就是不可用?
三、峰回路转:华为技术支持揭示真相
就在我几乎要放弃的时候,我决定直接联系华为的技术支持团队。非常幸运的是,华为技术老师非常热心,专门为我安排了一次线上技术交流会议。
问题的真相:phone scope 仅限游戏应用
在会议中,华为技术老师一针见血地指出了问题所在:
phonescope 仅适用于游戏类应用,普通应用无法通过createAuthorizationWithHuaweiIDRequest获取手机号权限!
这句话如同醍醐灌顶,一下子解开了困扰我一个月的谜团。原来:
-
权限级别不同:
- 游戏应用:可以通过
phonescope 直接获取手机号 - 普通应用:不能使用
phonescope
- 游戏应用:可以通过
-
设计原因:
- 华为为了保护用户隐私,对不同类型应用设置了不同的权限级别
- 手机号属于高度敏感的个人信息,需要更严格的授权流程
-
文档说明不够明确:
- 官方文档中对
phonescope 的使用限制说明不够突出 - 容易让开发者误以为所有应用都可以使用
- 官方文档中对
正确的方案:使用华为内置登录组件
华为技术老师告诉我,普通应用要获取用户手机号,必须使用华为提供的专用登录组件:
loginComponentManager:华为提供的登录组件管理器LoginWithHuaweiIDButton:华为官方的登录按钮 UI 组件
通过这两个组件获取的授权码(authorizationCode),才能在服务端调用华为接口换取真实的用户手机号。
关键点:
- 必须使用华为提供的 UI 组件
- 用户必须能够清楚地看到授权内容
- 用户必须主动点击授权按钮
- 通过组件获取的授权码才有获取手机号的权限
- 可以同时获取用户的头像和昵称
这样的设计确保了用户的知情权和选择权,但也增加了开发的复杂度。
四、新的挑战:uni-app 如何调用鸿蒙原生组件?
得知了正确的方案后,我面临一个新的问题:华为技术老师没有提供 uni-app 的集成方案。
华为官方文档中的示例都是基于原生鸿蒙开发的,使用的是 ArkTS 语言。而我们的项目是 uni-app 框架,如何在 uni-app 中调用鸿蒙原生组件呢?
自主探索:研究 DCloud 官方文档
既然没有现成的方案,我只能自己探索。我开始研究 DCloud 官方文档,找到了关键的一篇文档:
这篇文档详细介绍了如何在 uni-app 中通过 <embed> 标签调用鸿蒙原生组件。关键要点:
- 使用
<embed>标签:uni-app 提供的特殊标签,用于嵌入原生组件 tag属性:指定原生组件的标识options属性:传递给原生组件的配置参数- 事件监听:通过
@success、@fail等监听原生组件的事件
实现方案:封装华为登录组件
基于 DCloud 的文档和示例,我开始着手实现 uni-app 调用华为登录组件的方案。
1. 原生侧实现(ArkTS)
在 uni_modules/anhao-login/utssdk/app-harmony/login.ets 中实现登录组件的封装:
import { defineNativeEmbed, NativeEmbedBuilderOptions } from '@dcloudio/uni-app-runtime'
import { authentication, loginComponentManager, LoginWithHuaweiIDButton } from '@kit.AccountKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { BusinessError } from '@kit.BasicServicesKit'
// 定义参数接口
interface QuickLoginOptions extends NativeEmbedBuilderOptions {
}
// 定义返回数据接口
interface QuickLoginSuccessDetail {
authorizationCode?: string
unionID?: string
openID?: string
success: boolean
err: string
}
interface QickLoginEvent {
type: string
detail: QuickLoginSuccessDetail
}
// 定义登录组件
@Component
struct QuickLoginComponent {
onSuccess?: Function
onFail?: Function
// 创建登录组件控制器
private controller: loginComponentManager.LoginWithHuaweiIDButtonController =
new loginComponentManager.LoginWithHuaweiIDButtonController()
// 设置协议状态为已同意(实际应用中可根据需求调整)
.setAgreementStatus(loginComponentManager.AgreementStatus.ACCEPTED)
// 设置点击登录按钮的回调
.onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {
if (error) {
// 登录失败
hilog.error(0x0000, 'QuickLogin', `failed: ${error.code} ${error.message}`)
if (this.onFail) {
const detail = {
success: false,
err: `failed: ${error.code} ${error.message}`
} as QuickLoginSuccessDetail
const res = {
type: "quickLogin",
detail: detail
} as QickLoginEvent
this.onFail(res)
}
} else {
// 登录成功
hilog.info(0x0000, 'QuickLogin', `success: ${response.authorizationCode}`)
if (this.onSuccess) {
const detail = {
authorizationCode: response.authorizationCode, // 授权码
unionID: response.unionID, // 用户统一ID
openID: response.openID, // 应用内用户ID
success: true,
err: 'ok',
} as QuickLoginSuccessDetail
const res = {
type: "quickLogin",
detail: detail
} as QickLoginEvent
this.onSuccess(res)
}
}
})
.onClickEvent((error: BusinessError, clickEvent: loginComponentManager.ClickEvent) => {
hilog.info(0x0000, 'testTag', `QuickLogin clickEvent: ${clickEvent}`);
});
build() {
// 创建华为登录按钮
LoginWithHuaweiIDButton({
params: {
style: loginComponentManager.Style.BUTTON_CUSTOM, // 按钮样式
loginType: loginComponentManager.LoginType.QUICK_LOGIN, // 登录类型:一键登录
supportDarkMode: true, // 支持深色模式
},
controller: this.controller
})
.width('100%')
.height('100%')
}
}
// 定义构建器
@Builder
function QuickLoginBuilder(opts: QuickLoginOptions) {
QuickLoginComponent({
onSuccess: opts?.on?.get('success'),
onFail: opts?.on?.get('fail')
})
.width(opts.width)
.height(opts.height)
}
// 注册原生组件,标识为 'hwilogin'
defineNativeEmbed('hwilogin', { builder: QuickLoginBuilder })
关键技术点:
- 使用
defineNativeEmbed:这是 DCloud 提供的 API,用于注册原生组件 loginComponentManager.LoginWithHuaweiIDButtonController:华为提供的登录控制器LoginWithHuaweiIDButton:华为官方的登录按钮组件loginType: QUICK_LOGIN:设置为一键登录类型,可以获取手机号onClickLoginWithHuaweiIDButton:登录成功后的回调,返回授权码、unionID、openID- 事件传递:通过
onSuccess和onFail将结果传递给 uni-app
2. uni-app 侧调用
在 uni-app 中,通过 <embed> 标签调用原生组件:
<template>
<view class="container">
<!-- 使用 embed 标签嵌入华为登录按钮 -->
<embed
class="login-button"
tag="hwilogin"
:options="options"
@success="loginSuccess"
@fail="loginFail"
></embed>
<view class="user-info" v-if="userInfo.phone">
<text>手机号:{{ userInfo.phone }}</text>
<text>unionID:{{ userInfo.unionID }}</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import '@/uni_modules/anhao-login'
const options = ref({})
const userInfo = ref({
phone: '',
unionID: '',
openID: ''
})
const loginSuccess = ({ detail }) => {
console.log('登录成功:', detail)
// detail 结构:
// {
// authorizationCode: "xxx", // 授权码(关键!)
// unionID: "xxx", // 用户统一ID
// openID: "xxx", // 应用内用户ID
// success: true,
// err: "ok"
// }
userInfo.value.unionID = detail.unionID
userInfo.value.openID = detail.openID
// 将授权码发送到服务端,换取真实手机号
getPhoneFromServer(detail.authorizationCode)
}
const loginFail = (err) => {
console.error('登录失败:', err)
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
}
// 调用服务端接口获取真实手机号
const getPhoneFromServer = async (code) => {
try {
const res = await uni.request({
url: 'https://your-server.com/api/getPhoneNumber',
method: 'POST',
data: {
code: code
}
})
if (res.data.success) {
userInfo.value.phone = res.data.phoneNumber
uni.showToast({
title: '登录成功',
icon: 'success'
})
}
} catch (error) {
console.error('获取手机号失败:', error)
uni.showToast({
title: '获取手机号失败',
icon: 'none'
})
}
}
</script>
<style scoped>
.container {
padding: 100px 20px;
}
.login-button {
display: block;
width: 200px;
height: 50px;
margin: 10px auto;
}
.user-info {
margin-top: 40px;
text-align: center;
}
</style>
技术难点与解决方案
在实现过程中,我遇到了一些技术难点:
难点 1:原生组件的生命周期管理
问题:鸿蒙原生组件有自己的生命周期,如何与 uni-app 的页面生命周期同步?
解决方案:
- 使用
@Component装饰器定义组件 - 在
build()方法中创建 UI - 通过
defineNativeEmbed注册后,uni-app 会自动管理组件生命周期
难点 2:事件通信机制
问题:原生组件的事件如何传递到 uni-app 侧?
解决方案:
- 在原生侧,通过
onSuccess和onFail回调函数传递事件 - 使用
opts?.on?.get('success')获取 uni-app 传递的事件监听器 - uni-app 侧通过
@success、@fail监听事件
难点 3:参数传递
问题:uni-app 如何向原生组件传递参数?
解决方案:
- 通过
:options属性传递配置参数 - 原生侧通过
NativeEmbedBuilderOptions接口接收参数 - 支持动态更新参数(通过响应式数据)
难点 4:组件样式定制
问题:华为登录按钮的样式如何定制?
解决方案:
- 使用
loginComponentManager.Style.BUTTON_CUSTOM自定义样式 - 通过
.width()和.height()设置组件尺寸 - 可以在外层包裹自定义样式
五、服务端集成:获取真实手机号
客户端获取授权码后,还需要服务端配合才能获取真实手机号。这是整个流程中最关键的一步。
华为服务端接口
接口地址:
POST https://account-api.cloud.huawei.com/oauth2/v6/quickLogin/getPhoneNumber
请求头:
Content-Type: application/json;charset=UTF-8
请求参数:
{
"code": "<authorizationCode>",
"clientId": "<your-clientId>",
"clientSecret": "<your-clientSecret>"
}
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| code | String | 是 | 客户端通过登录组件获取的授权码 |
| clientId | String | 是 | 应用的 clientId |
| clientSecret | String | 是 | 应用的 clientSecret(必须保密!) |
响应示例:
{
"openId": "xxxx",
"unionId": "xxxx",
"phoneNumber": "13111111111",
"phoneNumberValid": 1,
"purePhoneNumber": "13111111111",
"phoneCountryCode": "0086"
}
服务端实现示例
方案一:PHP 实现
<?php
header('Content-Type: application/json;charset=UTF-8');
// 获取客户端传递的授权码
$requestData = json_decode(file_get_contents('php://input'), true);
$code = $requestData['code'] ?? '';
// 验证授权码
if (empty($code)) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => '缺少授权码'
]);
exit;
}
// 华为 API 配置(从环境变量或配置文件读取)
$clientId = getenv('HUAWEI_CLIENT_ID');
$clientSecret = getenv('HUAWEI_CLIENT_SECRET');
// 调用华为接口获取手机号
$url = 'https://account-api.cloud.huawei.com/oauth2/v6/quickLogin/getPhoneNumber';
$postData = json_encode([
'code' => $code,
'clientId' => $clientId,
'clientSecret' => $clientSecret
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json;charset=UTF-8'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
$result = json_decode($response, true);
// 返回手机号给客户端
echo json_encode([
'success' => true,
'phoneNumber' => $result['phoneNumber'],
'phoneCountryCode' => $result['phoneCountryCode'],
'unionId' => $result['unionId'],
'openId' => $result['openId']
]);
} else {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => '获取手机号失败',
'error' => $response
]);
}
?>
安全注意事项
⚠️ 非常重要:
-
clientSecret 必须保存在服务端
- 绝对不能写在客户端代码中
- 应该使用环境变量或密钥管理服务
- 定期更换 clientSecret
-
授权码验证
- 授权码只能使用一次
- 授权码有效期很短(通常几分钟)
- 服务端应记录已使用的授权码,防止重放攻击
-
防刷机制
- 限制同一用户的请求频率
- 记录异常请求日志
- 必要时添加图形验证码
-
HTTPS 传输
- 所有接口必须使用 HTTPS
- 防止授权码在传输过程中被窃取
-
数据存储
- 手机号等敏感信息应加密存储
- 遵守数据保护法规(如 GDPR、个人信息保护法)
六、完整功能实现
基于以上技术方案,插件最终实现了三个核心功能:
1. 获取匿名手机号
快速获取用户的脱敏手机号(如:131******23),无需用户额外授权:
import { getHmAnonymousPhone } from '@/uni_modules/anhao-login'
getHmAnonymousPhone({
success(res) {
console.log('脱敏手机号:', res.quickLoginAnonymousPhone)
console.log('openID:', res.openID)
console.log('unionID:', res.unionID)
console.log('本机号码一致性:', res.localNumberConsistency)
},
fail(err) {
console.error('获取失败:', err)
}
})
适用场景:
- 快速注册场景
- 用户身份初步识别
- 本机号码一致性检测
2. 获取用户头像昵称
通过引导用户授权,获取用户的基础信息:
import { hmLoginWithProfile } from '@/uni_modules/anhao-login'
hmLoginWithProfile({
success(res) {
console.log('昵称:', res.nickName)
console.log('头像:', res.avatarUri)
console.log('unionID:', res.unionID)
},
fail(err) {
console.error('获取失败:', err)
}
})
适用场景:
- 用户资料完善
- 社交应用的用户展示
- 个性化推荐
3. 华为账号一键登录(核心功能)
通过华为内置登录组件,获取授权码,同时可以获取用户的头像和昵称,再通过服务端接口获取真实手机号。
完整流程:
用户点击登录按钮
↓
调用华为登录组件
↓
用户确认授权
↓
获取 authorizationCode、unionID、openID
(可同时获取头像、昵称)
↓
发送授权码到服务端
↓
服务端调用华为接口
↓
获取真实手机号
↓
返回给客户端
↓
登录成功
特别说明:
- 在用户点击登录按钮授权后,除了获取
authorizationCode,还可以同时获取用户的头像和昵称 - 这样可以一次授权完成用户的完整信息获取,无需多次交互
- 大大提升了用户体验和开发效率
七、插件封装与开源
为什么要做成插件
在解决了集成问题后,我意识到:
- 这个问题具有普遍性:很多 uni-app 开发者可能都会遇到同样的问题
- 华为登录是刚需:鸿蒙应用都需要用户登录功能
- 没有现成的方案:uni-app 生态中缺少成熟的华为登录插件
- 技术门槛较高:涉及原生开发,对普通开发者不够友好
因此,我决定将这个功能封装成标准的 uni-app 插件,并开源出来,让更多开发者能够快速集成华为账号登录功能。
开源地址
为了方便开发者使用和贡献,插件已经在多个平台开源:
- DCloud 插件市场: https://ext.dcloud.net.cn/plugin?id=25834
- GitCode 开源仓库:https://gitcode.com/ALAPI/uniapp-hwlogin
欢迎大家使用、提 Issue 和 PR,共同完善这个插件!
八、开发心得与经验总结
回顾整个集成过程,我有以下几点深刻体会:
1. 不要轻易怀疑官方文档,但也不要完全依赖
华为的官方文档提供了很多有价值的信息,但对于一些特殊限制(如 phone scope 仅限游戏应用),说明可能不够突出。
经验教训:
- 遇到问题时,先仔细阅读官方文档,特别是"注意事项"和"权限说明"部分
- 如果文档无法解决,及时联系官方技术支持
- 多参考官方示例代码,理解推荐的实现方式
2. 理解平台设计理念很重要
华为限制 phone scope 的使用,并不是故意增加开发难度,而是为了保护用户隐私:
- 手机号是高度敏感的个人信息
- 用户必须清楚知道自己在授权什么
- 通过 UI 组件授权,确保用户的知情权
经验教训:
- 理解并尊重平台的安全机制
- 不要尝试绕过安全限制
- 站在用户隐私保护的角度思考问题
3. 跨平台开发需要深入原生
uni-app 虽然提供了跨平台能力,但在集成平台特有功能时,仍然需要深入原生层:
- 理解鸿蒙原生组件的机制
- 掌握 uni-app 的原生扩展方式
- 熟悉原生代码与 JS 的通信机制
经验教训:
- 不要指望所有功能都能通过纯 JS 实现
- 学习平台原生开发知识是必要的
- 善用 DCloud 提供的原生扩展机制
4. 没有现成方案就自己创造
华为技术老师没有提供 uni-app 的集成方案,但这不是放弃的理由:
- DCloud 提供了完善的原生组件调用文档
- 社区有很多开发者分享的经验
- 通过学习和实践,我们也能创造自己的方案
经验教训:
- 遇到技术挑战,先思考是否有类似的解决方案可以参考
- 善用搜索引擎和开发者社区
- 不要害怕深入原生层,这是技术成长的必经之路
5. 安全性永远是第一位
在整个实现过程中,安全性始终是最重要的考虑因素:
clientSecret必须保存在服务端- 授权码必须做防重放处理
- 用户数据传输必须使用 HTTPS
经验教训:
- 永远不要将密钥写在客户端代码中
- 敏感操作必须在服务端完成
- 为授权码设置有效期和使用次数限制
- 定期进行安全审计
6. 开源是最好的学习和回馈方式
在解决问题的过程中,我参考了很多开源项目和社区讨论。当我自己解决了问题后,我也选择将方案开源:
- 帮助他人,节省他们的时间
- 收到反馈,促进自己的成长
- 建立口碑,融入开发者社区
- 共同建设更好的鸿蒙生态
开源的价值:
- 知识的传播和共享
- 技术的迭代和改进
- 社区的繁荣和发展
- 个人的成长和提升
7. 感恩与协作
这次集成之所以能成功,离不开很多人的帮助:
- 华为技术老师的耐心指导,解答了关键问题
- DCloud 提供的完善文档和示例
- 社区开发者的经验分享和反馈
感恩:
- 感谢华为技术团队为开发者提供的支持
- 感谢 DCloud 搭建的跨平台开发生态
- 感谢所有为鸿蒙生态建设贡献力量的开发者
协作:
- 多参与社区讨论,分享经验
- 遇到问题时,整理成文档帮助后来人
- 对他人的开源项目表示支持和感谢
- 共同推动鸿蒙生态的发展
九、写在最后
从一个月前的困惑,到今天的豁然开朗,再到最终的开源贡献,这段经历让我深刻体会到:
技术问题没有解决不了的,关键是找对方法和寻求帮助。
回顾这次经历,最大的收获不仅仅是解决了一个技术问题,更重要的是:
- 学会了正确的求助方式:遇到问题时,先自己尝试,实在解决不了就及时联系官方技术支持
- 理解了平台设计理念:安全和隐私保护永远是第一位的
- 掌握了原生集成能力:在 uni-app 中调用鸿蒙原生组件
- 建立了开源思维:用开源的方式回馈社区
- 感受到了协作的力量:一个人走得快,一群人走得远
鸿蒙生态正处于快速发展阶段,还有很多开放能力等待我们去探索和实践。作为 uni-app 开发者,我们既能享受跨平台开发的便利,又能深入集成各平台的原生能力。
希望这篇文章能帮助到正在或即将集成华为账号服务的开发者们。如果你在使用插件的过程中遇到任何问题,欢迎通过以下方式联系我:
- DCloud 插件市场: https://ext.dcloud.net.cn/plugin?id=25834
- GitCode Issues: https://gitcode.com/ALAPI/uniapp-hwlogin/issues
让我们一起,为鸿蒙生态的繁荣贡献自己的力量!
附录:关键技术点总结
错误方案 vs 正确方案
| 对比项 | 错误方案 | 正确方案 |
|---|---|---|
| API 调用 | createAuthorizationWithHuaweiIDRequest |
loginComponentManager + LoginWithHuaweiIDButton |
| 权限申请 | scopes: ['phone'] |
通过登录组件自动处理 |
| 适用范围 | 仅游戏应用 | 普通应用 |
| UI 要求 | 无 | 必须使用华为提供的 UI 组件 |
| 授权码权限 | 无法获取手机号 | 可以获取手机号 |
| 获取信息 | 有限 | 可同时获取手机号、头像、昵称、unionID、openID |
uni-app 调用原生组件关键点
- 使用
defineNativeEmbed注册原生组件 - 设置
tag属性 为原生组件标识(如hwilogin) - 通过
:options传递配置 参数 - 通过
@success、@fail监听事件 - 使用
<embed>标签 嵌入原生组件 - 处理原生组件的生命周期 和事件传递
服务端集成关键点
- clientSecret 必须保存在服务端
- 授权码只能使用一次
- 授权码有效期很短(几分钟)
- 必须使用 HTTPS 传输
- 添加防刷机制
- 记录已使用的授权码,防止重放攻击
- 敏感数据加密存储
华为登录组件配置要点
LoginWithHuaweiIDButton({
params: {
style: loginComponentManager.Style.BUTTON_CUSTOM, // 按钮样式
loginType: loginComponentManager.LoginType.QUICK_LOGIN, // 登录类型:一键登录
supportDarkMode: true, // 支持深色模式
},
controller: this.controller
})
关键参数:
style:按钮样式,可选BUTTON_BLUE、BUTTON_WHITE、BUTTON_CUSTOMloginType:登录类型,QUICK_LOGIN表示一键登录,可获取手机号supportDarkMode:是否支持深色模式
关于作者
一名热爱开源的 uni-app 开发者,专注于跨平台应用开发和鸿蒙生态探索。在摸索中成长,在分享中进步。
相关链接
- anhao-login 插件市场: https://ext.dcloud.net.cn/plugin?id=25834
- anhao-login 开源仓库:https://gitcode.com/ALAPI/uniapp-hwlogin
- uni-app 调用鸿蒙原生组件:https://uniapp.dcloud.net.cn/tutorial/harmony/native-component.html
- 华为账号服务文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/account-phone-unionid-login
- uni-app 官方文档:https://uniapp.dcloud.net.cn/