在鸿蒙(HarmonyOS)系统中开通认证服务 如何使用自己设计的UI界面和修改个人信息(例:上传头像)的步骤如下:

2025-06-29 10:36:14
121次阅读
0个评论

1. 准备工作

1. 注册华为开发者账号

  • 访问华为开发者联盟官网并注册账号。
  • 完成实名认证(个人或企业开发者),需提供身份证或企业资质信息。

2. 登录AppGallery Connect (AGC)

  • 使用华为开发者账号登录AGC控制台
  • 创建或选择已有项目。

3. 开通认证服务

  • 在项目设置中,点击左侧菜单的 “认证服务”
  • 选择需要开通的认证方式(如手机验证码、邮箱、生物识别等)并启用。

4. 配置端侧项目

  • 添加依赖:在oh-package.json5中引入相关SDK,如:
"dependencies": {
  "@hw-agconnect/cloud": "^1.0.0",
  "@hw-agconnect/hmcore": "^1.0.0"
}
  • 开通网络权限:在module.json5中申请INTERNET权限。
  • 更新agconnect-services.json:从AGC下载最新配置文件并集成到项目中。

5. 登录页面实现 (MyLogin.ets)

5.1 基础界面代码

@Entry
@Component
struct MyLogin {
  // 状态变量声明
  @State phoneNumber: string = '';  //手机号
  @State verifyCode: string = '';   //验证码

  build() {
    Column({space:10}){
      Text('登录').fontSize(24)
      Divider()
      Row(){
        TextInput({ placeholder: '请输入手机号' })
          .width(160)
          .onChange((val) => { 
            this.phoneNumber = val // 绑定手机号输入
          }) 
      }
      Row(){
        TextInput({placeholder:'请输入验证码'})
          .width(150)
          .onChange((val)=>{
            this.verifyCode = val // 绑定验证码输入
          }) 
        Button('获取验证码').onClick(async ()=>{ 
          /* 调用5.2逻辑 */
        })
      }
      Button('登录').onClick(async () => {
        /* 调用5.3逻辑 */
      })
    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

说明

  • 使用ArkUI声明式语法构建登录界面,包含手机号和验证码输入框
  • 通过@State实现数据响应式绑定,输入内容实时更新状态变量

5.2 验证码功能实现

        Button('获取验证码')
          .onClick(async ()=>{
            try {
              await cloud.auth().requestVerifyCode({
                verifyCodeType: {
                  kind: 'phone',
                  phoneNumber: this.phoneNumber,  // 获取用户输入的手机号
                  countryCode: '86'
                },
                action: VerifyCodeAction.REGISTER_LOGIN,
                lang: 'zh_CN',
                sendInterval: 60
              })
              promptAction.showToast({ message: '验证码已发送' })
            } catch (error) {
              promptAction.showToast({ message: `发送失败: ${error.message}` })
            }
          })

说明

  • requestVerifyCode方法向指定手机号发送验证码
  • sendInterval参数防止频繁发送,提升安全性

5.3 登录功能实现

  @State verifyCode:string = ''  // 验证码
  @StorageLink('user')user:AuthUser | null= null //使用 AppStorage 保存当前登录的用户信息
  
      
      Button('登录')
        .onClick(async () => {
          try {
            const  result = await cloud.auth().signIn({
              credentialInfo: {
                kind: 'phone',
                countryCode: '86',
                phoneNumber: this.phoneNumber, // 获取用户输入的手机号
                verifyCode: this.verifyCode  // 获取用户输入的验证码
              }
            })
            const user = result.getUser() // 用户信息
            AppStorage.setOrCreate('user',user) // 保存用户信息
            router.replaceUrl({url:'pages/MyInfo'})
            promptAction.showToast({ message: '登录成功' })
          } catch (error) {
            console.error('登录失败:', error)
            promptAction.showToast({ message: '登录失败' })
          }
        })

说明

  • signIn方法通过手机号+验证码完成登录验证
  • AppStorage实现用户信息跨页面持久化

5.4处理异常退出后无法登录

  async aboutToAppear(): Promise<void> {
    // 防止异常退出应用无法登录
    try {
      const user = await cloud.auth().getCurrentUser()
      if (user != null) {
        AppStorage.setOrCreate('user',user)
        router.replaceUrl({url:'pages/MyInfo'})
      }
    }catch (e){

    }
  }

6. 用户信息页面 (MyInfo.ets)

6.1 基础界面

@State username: string = '';
@State photoUrl: string = '';
@State uris: Array<string> = []; // 新增:图片URI数组
@StorageLink('user') user: AuthUser | null = null;

build() {
  Column({space:10}){
    Image(this.photoUrl == undefined?$r('app.media.user_dark'):this.photoUrl)
      .width(70)
      .onClick(()=>{
        this.getFileAssetsFromType()  // 调用6.3逻辑
      }) 
    Row(){
      Text('昵称:')
      TextInput({placeholder:`${this.username}`})
        .onChange((val)=>{ 
          this.username = val
        })
    }
    Button('保存').onClick(async () => {
      /* 调用6.4逻辑 */ 
    })
    Button('退出登录').onClick(async ()=>{
      /* 调用6.5逻辑 */
    })
  }
  .height('100%')
  .width('100%')
}

说明

  • 通过条件渲染显示用户头像或默认占位图
  • 点击头像触发文件选择器

6.2 用户信息获取

  @State username: string = '';  //用户昵称
  @State photoUrl : string = '' //用户头像
  @StorageLink('user')user:AuthUser | null= null // 登录的用户信息

    aboutToAppear(): void {
    // 1.
    cloud.auth().getCurrentUser()  // 方法一直接获取当前登录的用户信息
  
    // 2.AppStorage               使用上一个登录页面的 AppStorage 保存当前登录的用户信息
    this.photoUrl = this.user?.getPhotoUrl() as string
    this.username = this.user?.getDisplayName() as string

  }

说明

  • aboutToAppear生命周期钩子在页面显示前初始化数据
  • 优先从AppStorage获取用户信息,减少网络请求

6.3 头像上传功能

  /**
   *  图片上传
   */
  getFileAssetsFromType(){
    //  获取图片选择对象实例
    const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions()
    //  定义当前图片选择器选择图片的最大数量
    photoSelectOptions.maxSelectNumber = 1
    //  定义选择数据的类型
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE
    //  创建图库选择器实例
    const photoviewPicker = new photoAccessHelper.PhotoViewPicker()
    photoviewPicker.select(photoSelectOptions)
      .then((value)=>{
        // 获取到选择图片的路径,数组类型数据
        this.uris = value.photoUris
        this.photoCount = this.uris.length
        this.photoUrl = this.uris[0]
      }).catch((err:BusinessError)=>{
      console.error('err',JSON.stringify(err))
      return
    })
  }

       Image(this.photoUrl == undefined?$r('app.media.user_dark'):this.photoUrl)  // 设置默认头像
        .width(70)
        .onClick(()=>{
          this.getFileAssetsFromType()  // 调用上传头像方法
        })

说明

  • 使用系统相册选择器获取本地图片URI

6.4 数据保存功能

      Button('保存')
        .onClick(async () => {
          if (!this.user) {
            promptAction.showToast({ message: '用户未登录' });
            return;
          }
          try {
            await this.user.updateProfile({
              displayName: this.username, // 获取修改后的用户名
              photoUrl: this.photoUrl   // 获取修改后的头像
            });
            promptAction.showToast({ message: '保存成功' });
          } catch (e) {
            console.error(e); // 打印错误信息到控制台
            promptAction.showToast({ message: '保存失败: ' + e.message });
          }
        })

说明

  • 保存前校验用户登录状态
  • updateProfile需配合云存储使用

6.5 退出登录

Button('退出登录')
  .onClick(async ()=>{
    try {
      await cloud.auth().signOut();
      this.user = null; // 清空本地用户状态
      router.back(); // 返回上一页
    } catch (error) {
      console.error('退出失败:', error);
    }
  })

说明

  • signOut清除本地认证令牌
  • 同步更新全局存储中的用户状态

补充说明:

  1. 状态管理
    • @State用于组件内响应式数据,@StorageLink实现跨组件数据共享
    • 复杂场景建议使用全局状态管理(如Redux模式)
  2. 调试建议
    • 真机测试需确保AGC项目配置正确的OAuth 2.0回调地址
    • 使用console.log打印用户对象结构,验证数据获取
  • 结合华为云服务实现端云一体化认证。

如需更详细的代码示例或高级配置(如生物识别认证),可参考华为官方文档或相关教程。

完整代码和模拟器演示:

1.登录页面实现 (MyLogin.ets)

import cloud, { AuthUser, VerifyCodeAction } from '@hw-agconnect/cloud'
import { promptAction, router } from '@kit.ArkUI'


@Entry
@Component
struct MyLogin {
  @State phoneNumber:string = ''
  @State verifyCode:string = ''
  @StorageLink('user')user:AuthUser | null= null

  async aboutToAppear(): Promise<void> {
    // 防止异常退出应用无法登录
    try {
      const user = await cloud.auth().getCurrentUser()
      if (user != null) {
        AppStorage.setOrCreate('user',user)
        router.replaceUrl({url:'pages/MyInfo'})
      }
    }catch (e){

    }
  }

  build() {
    Column({space:10}){
      Text('登录')
        .fontSize(24)
      Divider()
      Row(){
        TextInput({placeholder:'请输入手机号'})
          .width(260)
          .onChange((val)=>{
            this.phoneNumber = val
          })
      }
      Row(){
        TextInput({placeholder:'请输入验证码'})
          .width(150)
          .onChange((val)=>{
            this.verifyCode = val
          })
        Button('获取验证码')
          .onClick(async ()=>{
            try {
              await cloud.auth().requestVerifyCode({
                verifyCodeType: {
                  kind: 'phone',
                  phoneNumber: this.phoneNumber,
                  countryCode: '86'
                },
                action: VerifyCodeAction.REGISTER_LOGIN,
                lang: 'zh_CN',
                sendInterval: 60
              })
              promptAction.showToast({ message: '验证码已发送' })
            } catch (error) {
              promptAction.showToast({ message: `发送失败: ${error.message}` })
            }
          })
      }
      Button('登录')
        .onClick(async () => {
          try {
            const  result = await cloud.auth().signIn({
              credentialInfo: {
                kind: 'phone',
                countryCode: '86',
                phoneNumber: this.phoneNumber,
                verifyCode: this.verifyCode
              }
            })
            const user = result.getUser() // 用户信息
            AppStorage.setOrCreate('user',user) // 保存用户信息
            router.replaceUrl({url:'pages/MyInfo'})
            promptAction.showToast({ message: '登录成功' })
          } catch (error) {
            console.error('登录失败:', error)
            promptAction.showToast({ message: '登录失败' })
          }
        })

    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}


/**
 * user.getuid()  //获取用户唯一标识
 *user.getPhone()  //获取手机号
 *user.getEmail()  // 获取邮箱
 *user.getUserExtra() //获取用户创建时间 和 最近一次的登录时间
 * user.getPhotoUrl()  // 头像
 * user.getDisplayName()  //昵称
 */

2.用户信息页面 (MyInfo.ets)

import cloud, { AuthUser } from '@hw-agconnect/cloud';
import { promptAction, router } from '@kit.ArkUI';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { error } from '@hw-agconnect/hmcore/src/main/ets/error/CoreErrorCode';

@Entry
@Component
struct MyInfo {
  @State username: string = '';  //用户昵称
  @State photoUrl : string = '' //用户头像
  @StorageLink('user')user:AuthUser | null= null // 登录的用户信息
  @State uris:Array<string> = [] // 从图库获取的图片路径组
  @State photoCount:number = 0

  aboutToAppear(): void {
    // 1.
    cloud.auth().getCurrentUser()
    // 2.AppStorage
    this.photoUrl = this.user?.getPhotoUrl() as string
    console.log('useruser',`${this.photoUrl}`);
    this.username = this.user?.getDisplayName() as string
    console.log('useruser',JSON.stringify(this.user?.getDisplayName()));
    console.log('useruser',`${this.username}`);
  }


  /**
   *  图片上传
   */
  getFileAssetsFromType(){
    //  获取图片选择对象实例
    const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions()
    //  定义当前图片选择器选择图片的最大数量
    photoSelectOptions.maxSelectNumber = 1
    //  定义选择数据的类型
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE
    //  创建图库选择器实例
    const photoviewPicker = new photoAccessHelper.PhotoViewPicker()
    photoviewPicker.select(photoSelectOptions)
      .then((value)=>{
        // 获取到选择图片的路径,数组类型数据
        this.uris = value.photoUris
        this.photoCount = this.uris.length
        this.photoUrl = this.uris[0]
      }).catch((err:BusinessError)=>{
      console.error('err',JSON.stringify(err))
      return
    })
  }


  build() {
    Column({space:10}){
      Image(this.photoUrl == undefined?$r('app.media.user_dark'):this.photoUrl)
        .width(70)
        .onClick(()=>{
          this.getFileAssetsFromType()
          this.photoUrl = this.uris[0]
        })
      Row(){
        Text('昵称:')
        TextInput({placeholder:`${this.username}`})
          .onChange((val)=>{
            this.username = val
          })
      }
      Button('保存')
        .onClick(async () => {
          if (!this.user) {
            promptAction.showToast({ message: '用户未登录' });
            return;
          }
          try {
            await this.user.updateProfile({
              displayName: this.username,
              photoUrl: this.photoUrl
            });
            promptAction.showToast({ message: '保存成功' });
          } catch (e) {
            console.error(e); // 打印错误信息到控制台
            promptAction.showToast({ message: '保存失败: ' + e.message });
          }
        })
      Button('退出登录')
        .onClick(async ()=>{
          try {
            await cloud.auth().signOut() // 退出登录
            router.replaceUrl({ url: 'pages/Index' })
            promptAction.showToast({ message: '保存成功' })
          }catch (e) {
          }
        })
    }
    .height('100%')
    .width('100%')
  }
}

3.演示效果

收藏00

登录 后评论。没有帐号? 注册 一个。