i***@alone88.cn
i***@alone88.cn
  • 发布:2025-11-12 20:51
  • 更新:2025-11-24 02:38
  • 阅读:1414

【鸿蒙征文】uni-app 鸿蒙开发实践:华为账号一键登录集成之路

分类:鸿蒙Next

完整示例截图

注意获取手机号需要企业账号才可以申请这个权限,个人帐号需要使用静默登陆获取 openid 进行登陆

前言

随着鸿蒙生态的快速发展,越来越多的开发者开始尝试将应用迁移到鸿蒙平台。作为一名 uni-app 开发者,我在将应用适配鸿蒙的过程中,遇到了用户登录这一基础但重要的需求。华为账号一键登录作为鸿蒙生态的重要开放能力,能够为用户提供便捷、安全的登录体验。但在实际集成过程中,我却经历了从迷茫到豁然开朗的曲折历程。

本文将分享我在 uni-app 项目中集成华为账号一键登录能力的完整过程,特别是如何获取用户真实手机号这个核心难题,包括走过的弯路、问题的根源、以及最终的自主实现方案。

一、需求场景与功能特点

应用场景

在开发鸿蒙应用时,用户登录是最基础也是最关键的功能。传统的登录方式存在诸多痛点:

  1. 短信验证码登录

    • 需要用户手动输入手机号
    • 等待验证码到达,体验不流畅
    • 可能遇到验证码延迟或收不到的问题
  2. 账号密码登录

    • 用户需要记忆密码
    • 首次使用需要注册流程
    • 密码找回流程复杂
  3. 第三方登录

    • 需要跳转第三方应用
    • 授权流程较长
    • 部分用户不信任第三方授权

而华为账号一键登录,完美解决了这些痛点:

核心应用场景

  • 📱 电商应用:快速注册登录,降低用户流失率
  • 🎮 游戏应用:一键登录游戏,快速进入游戏体验
  • 📰 内容平台:简化登录流程,提升内容消费体验
  • 💼 企业应用:安全可靠的身份认证
  • 🏥 生活服务:快速获取用户手机号,便于服务通知

功能特点

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 中已添加必要权限
  • 鸿蒙应用权限已正确声明

所有配置看起来都没有问题,但就是无法获取手机号权限。这个问题困扰了我整整一个月。

尝试过的各种方法

在这一个月里,我尝试了各种可能的解决方案:

  1. 重新申请开放能力:以为是审核有问题,重新申请了好几次
  2. 更换测试设备:换了不同的鸿蒙设备测试
  3. 重新生成签名:以为是签名配置问题
  4. 查阅官方文档:把账号服务相关文档翻了好几遍
  5. 搜索开发者论坛:看了很多类似问题的讨论
  6. 参考其他项目:找了一些开源项目的代码参考

但是,所有的尝试都以失败告终。我开始怀疑:是不是华为的这个 API 在 uni-app 环境下就是不可用?

三、峰回路转:华为技术支持揭示真相

就在我几乎要放弃的时候,我决定直接联系华为的技术支持团队。非常幸运的是,华为技术老师非常热心,专门为我安排了一次线上技术交流会议。

问题的真相:phone scope 仅限游戏应用

在会议中,华为技术老师一针见血地指出了问题所在:

phone scope 仅适用于游戏类应用,普通应用无法通过 createAuthorizationWithHuaweiIDRequest 获取手机号权限!

这句话如同醍醐灌顶,一下子解开了困扰我一个月的谜团。原来:

  1. 权限级别不同

    • 游戏应用:可以通过 phone scope 直接获取手机号
    • 普通应用:不能使用 phone scope
  2. 设计原因

    • 华为为了保护用户隐私,对不同类型应用设置了不同的权限级别
    • 手机号属于高度敏感的个人信息,需要更严格的授权流程
  3. 文档说明不够明确

    • 官方文档中对 phone scope 的使用限制说明不够突出
    • 容易让开发者误以为所有应用都可以使用

正确的方案:使用华为内置登录组件

华为技术老师告诉我,普通应用要获取用户手机号,必须使用华为提供的专用登录组件:

  1. loginComponentManager:华为提供的登录组件管理器
  2. LoginWithHuaweiIDButton:华为官方的登录按钮 UI 组件

通过这两个组件获取的授权码(authorizationCode),才能在服务端调用华为接口换取真实的用户手机号。

关键点

  • 必须使用华为提供的 UI 组件
  • 用户必须能够清楚地看到授权内容
  • 用户必须主动点击授权按钮
  • 通过组件获取的授权码才有获取手机号的权限
  • 可以同时获取用户的头像和昵称

这样的设计确保了用户的知情权和选择权,但也增加了开发的复杂度。

四、新的挑战:uni-app 如何调用鸿蒙原生组件?

得知了正确的方案后,我面临一个新的问题:华为技术老师没有提供 uni-app 的集成方案。

华为官方文档中的示例都是基于原生鸿蒙开发的,使用的是 ArkTS 语言。而我们的项目是 uni-app 框架,如何在 uni-app 中调用鸿蒙原生组件呢?

自主探索:研究 DCloud 官方文档

既然没有现成的方案,我只能自己探索。我开始研究 DCloud 官方文档,找到了关键的一篇文档:

uni-app 调用鸿蒙原生组件

这篇文档详细介绍了如何在 uni-app 中通过 <embed> 标签调用鸿蒙原生组件。关键要点:

  1. 使用 <embed> 标签:uni-app 提供的特殊标签,用于嵌入原生组件
  2. tag 属性:指定原生组件的标识
  3. options 属性:传递给原生组件的配置参数
  4. 事件监听:通过 @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 })

关键技术点

  1. 使用 defineNativeEmbed:这是 DCloud 提供的 API,用于注册原生组件
  2. loginComponentManager.LoginWithHuaweiIDButtonController:华为提供的登录控制器
  3. LoginWithHuaweiIDButton:华为官方的登录按钮组件
  4. loginType: QUICK_LOGIN:设置为一键登录类型,可以获取手机号
  5. onClickLoginWithHuaweiIDButton:登录成功后的回调,返回授权码、unionID、openID
  6. 事件传递:通过 onSuccessonFail 将结果传递给 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 侧?

解决方案

  • 在原生侧,通过 onSuccessonFail 回调函数传递事件
  • 使用 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  
    ]);  
}  
?>

安全注意事项

⚠️ 非常重要

  1. clientSecret 必须保存在服务端

    • 绝对不能写在客户端代码中
    • 应该使用环境变量或密钥管理服务
    • 定期更换 clientSecret
  2. 授权码验证

    • 授权码只能使用一次
    • 授权码有效期很短(通常几分钟)
    • 服务端应记录已使用的授权码,防止重放攻击
  3. 防刷机制

    • 限制同一用户的请求频率
    • 记录异常请求日志
    • 必要时添加图形验证码
  4. HTTPS 传输

    • 所有接口必须使用 HTTPS
    • 防止授权码在传输过程中被窃取
  5. 数据存储

    • 手机号等敏感信息应加密存储
    • 遵守数据保护法规(如 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,还可以同时获取用户的头像昵称
  • 这样可以一次授权完成用户的完整信息获取,无需多次交互
  • 大大提升了用户体验和开发效率

七、插件封装与开源

为什么要做成插件

在解决了集成问题后,我意识到:

  1. 这个问题具有普遍性:很多 uni-app 开发者可能都会遇到同样的问题
  2. 华为登录是刚需:鸿蒙应用都需要用户登录功能
  3. 没有现成的方案:uni-app 生态中缺少成熟的华为登录插件
  4. 技术门槛较高:涉及原生开发,对普通开发者不够友好

因此,我决定将这个功能封装成标准的 uni-app 插件,并开源出来,让更多开发者能够快速集成华为账号登录功能。

开源地址

为了方便开发者使用和贡献,插件已经在多个平台开源:

欢迎大家使用、提 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 搭建的跨平台开发生态
  • 感谢所有为鸿蒙生态建设贡献力量的开发者

协作

  • 多参与社区讨论,分享经验
  • 遇到问题时,整理成文档帮助后来人
  • 对他人的开源项目表示支持和感谢
  • 共同推动鸿蒙生态的发展

九、写在最后

从一个月前的困惑,到今天的豁然开朗,再到最终的开源贡献,这段经历让我深刻体会到:

技术问题没有解决不了的,关键是找对方法和寻求帮助。

回顾这次经历,最大的收获不仅仅是解决了一个技术问题,更重要的是:

  1. 学会了正确的求助方式:遇到问题时,先自己尝试,实在解决不了就及时联系官方技术支持
  2. 理解了平台设计理念:安全和隐私保护永远是第一位的
  3. 掌握了原生集成能力:在 uni-app 中调用鸿蒙原生组件
  4. 建立了开源思维:用开源的方式回馈社区
  5. 感受到了协作的力量:一个人走得快,一群人走得远

鸿蒙生态正处于快速发展阶段,还有很多开放能力等待我们去探索和实践。作为 uni-app 开发者,我们既能享受跨平台开发的便利,又能深入集成各平台的原生能力。

希望这篇文章能帮助到正在或即将集成华为账号服务的开发者们。如果你在使用插件的过程中遇到任何问题,欢迎通过以下方式联系我:

让我们一起,为鸿蒙生态的繁荣贡献自己的力量!


附录:关键技术点总结

错误方案 vs 正确方案

对比项 错误方案 正确方案
API 调用 createAuthorizationWithHuaweiIDRequest loginComponentManager + LoginWithHuaweiIDButton
权限申请 scopes: ['phone'] 通过登录组件自动处理
适用范围 仅游戏应用 普通应用
UI 要求 必须使用华为提供的 UI 组件
授权码权限 无法获取手机号 可以获取手机号
获取信息 有限 可同时获取手机号、头像、昵称、unionID、openID

uni-app 调用原生组件关键点

  1. 使用 defineNativeEmbed 注册原生组件
  2. 设置 tag 属性 为原生组件标识(如 hwilogin
  3. 通过 :options 传递配置 参数
  4. 通过 @success@fail 监听事件
  5. 使用 <embed> 标签 嵌入原生组件
  6. 处理原生组件的生命周期 和事件传递

服务端集成关键点

  1. clientSecret 必须保存在服务端
  2. 授权码只能使用一次
  3. 授权码有效期很短(几分钟)
  4. 必须使用 HTTPS 传输
  5. 添加防刷机制
  6. 记录已使用的授权码,防止重放攻击
  7. 敏感数据加密存储

华为登录组件配置要点

LoginWithHuaweiIDButton({  
  params: {  
    style: loginComponentManager.Style.BUTTON_CUSTOM,         // 按钮样式  
    loginType: loginComponentManager.LoginType.QUICK_LOGIN,   // 登录类型:一键登录  
    supportDarkMode: true,                                     // 支持深色模式  
  },  
  controller: this.controller  
})

关键参数

  • style:按钮样式,可选 BUTTON_BLUEBUTTON_WHITEBUTTON_CUSTOM
  • loginType:登录类型,QUICK_LOGIN 表示一键登录,可获取手机号
  • supportDarkMode:是否支持深色模式

关于作者

一名热爱开源的 uni-app 开发者,专注于跨平台应用开发和鸿蒙生态探索。在摸索中成长,在分享中进步。

相关链接

19 关注 分享
6***@qq.com 小疯子呵 DCloud_CHB CodeCrafter DCloud_UNI_JBB DCloud_uniCloud_CRL 哦哦哦哈哈 唐家三少 青衫行者 邵懒人 DCloud_UNI_CHB DCloud_云服务_moyang dcbottle3 人生ing 蜂医 威龙 用户2919468 g***@qq.com 巷子

要回复文章请先登录注册

i***@alone88.cn

i***@alone88.cn (作者)

回复 5***@qq.com :
比如这样, vue 里面加一个判断来动态显示

```vue
<!-- 华为登录按钮 -->
<view class="hw-login-wrapper">
<embed
v-if="agreeProtocol"
class="hw-login-button"
tag="hwilogin"
:options="options"
@success="loginSuccess"
>
<view v-else class="hw-login-button disabled" @click="handleHwLoginClick">
华为账号一键登录
</view>
</view>


```
2025-11-24 02:38
i***@alone88.cn

i***@alone88.cn (作者)

回复 5***@qq.com :
默认是true的呀, 你可以在 uniapp view 层,去判断是否同意隐私协议
2025-11-22 16:59
5***@qq.com

5***@qq.com

大佬,请问下如何通过options更新参数啊? 我通过options传递更新了个agree参数,ets组件的agree值还是停留到最初的fasle
2025-11-18 22:24
i***@alone88.cn

i***@alone88.cn (作者)

回复 loveCoding :
这个从中文搞到晚上 [捂脸]
2025-11-14 00:44
loveCoding

loveCoding

劳模啊,你这个文档就写了一天吧
2025-11-14 00:32
CodeCrafter

CodeCrafter

很棒!
2025-11-13 17:41
DCloud_CHB

DCloud_CHB

写的非常详细,技术人的榜样!
2025-11-13 13:50
小疯子呵

小疯子呵

棒,直接用插件可以一键手机号登录了
2025-11-12 23:24