Skip to content

证书、打包与实人认证(三) ​

本页对应 证书、打包与实人认证,共 6 段视频,约 1 小时 22 分。

学习目标 ​

  • 能说明一键登录中“用户授权”和“获取真实手机号”的区别,并独立完成授权按钮触发与授权结果解析。
  • 能创建并调用云函数,使用 uniCloud.callFunction 传递参数、接收返回值,并验证云函数执行结果。
  • 能配置一键登录所需参数与云函数依赖,调用 getPhoneNumber 获取用户手机号,并处理常见报错。
  • 能完成实人认证服务开通、服务空间绑定、模块启用及页面入口配置,并使用条件编译限制入口仅在 App 端显示。
  • 能编写实人认证云函数,接收姓名、身份证号、设备信息并返回认证所需 ID,能在客户端处理调用成功与失败。
  • 能调用实人认证摄像头完成认证流程,并根据结果实现“认证成功/认证失败”的提示或后续业务跳转。

知识点 ​

2.2 一键登录:从授权到拿到手机号 ​

一键登录是通过运营商(中国移动、中国电信、中国联通)获取用户手机号,进而完成登录。要明确:uni.login 唤起的是授权界面,用户点“同意”只是允许获取手机号,真正拿手机号还要靠云函数调用运营商接口。

2.2.1 开通服务 ​

为什么需要:没有开通一键登录服务,客户端无法使用 univerify,云函数也没有 apiKey、apiSecret。
怎么想:先按官方流程开通,再去配置应用。
怎么做:参见官方文档开通服务。
易错点:不要以为出现授权框就已经拿到手机号;授权只是第一步。

2.2.2 配置神领物流并重新打包 ​

为什么需要:应用要声明使用一键登录能力,否则调用会失败。
怎么想:在开发者中心配置应用信息,然后重新打包,让配置进入安装包。
怎么做:按下图配置神领物流,配置好后重新打包;自定义基座和正式包均可,开发阶段推荐自定义基座。

配置神领物流的一键登录服务

易错点:配置后不重新打包,手机上仍可能是旧配置。

2.2.3 客户端(App)调用 uni.login ​

为什么需要:App 端需要先唤起运营商授权界面,让用户确认授权。
怎么想:一键登录仅用于 App 端,所以登录页要用条件编译区分 App 与非 App。
怎么做:在 uni-app 中调用 uni.login,provider 指定为 univerify,univerifyStyle 定制授权界面样式。

javascript
uni.login({
  provider: 'univerify',
  univerifyStyle: {
    fullScreen: true,
  },
})
  • provider:指定登录服务类型,univerify 表示一键登录。
  • univerifyStyle:自定义授权界面的样式。

整合进神领物流登录页时,用条件编译处理执行逻辑:

vue
<!-- pages/login/index.vue -->
<script setup>
  import { ref, reactive, computed } from 'vue'
	
  // 省略中间部分代码...

  // 切换登录类型
  function changeLoginType() {
    // #ifdef APP-PLUS
    uni.login({
      provider: 'univerify',
      univerifyStyle: {
        fullScreen: true,
      },
    })
    // #endif

    // #ifndef APP-PLUS
    tabIndex.value = Math.abs(tabIndex.value - 1)
    // #endif
  }
</script>

上述代码仅会在 App 端调用 uni.login 唤起一键登录授权;非 App 端仍走原来的切换逻辑。转写稿中口述成 wx.login,这里应纠正为 uni.login。

自定义授权界面样式:

javascript
const { authResult } = await uni.login({
  provider: 'univerify',
  univerifyStyle: {
    fullScreen: true,
    icon: {
      path: 'static/logo.png', // 自定义显示在授权框中的logo,仅支持本地图片 默认显示App logo
    },
    authButton: {
      normalColor: '#ef4f3f', // 授权按钮正常状态背景颜色 默认值:#3479f5
      highlightColor: '#ef4f3f', // 授权按钮按下状态背景颜色 默认值:#2861c5(仅ios支持)
    },
    privacyTerms: {
      defaultCheckBoxState: false,
      uncheckedImage: 'static/images/uncheckedImage.png',
      checkedImage: 'static/images/checkedImage.png',
      termsColor: '#ef4f3f',
    },
  },
})

易错点:一键登录只能在 App 端用;网页、小程序要走条件编译分支。授权返回结果中可拿到 openid、access_token,但不是手机号本身。

2.2.4 云函数获取手机号 ​

为什么需要:手机号必须由服务端直接或间接调用运营商接口获取,不能只靠客户端授权框。
怎么想:把获取手机号的逻辑封装进云函数;客户端只把授权结果传给云函数,云函数再调用 uniCloud.getPhoneNumber。
怎么做:先开通 uniCloud 服务空间,再创建云函数。

新建云函数菜单

云函数创建对话框

默认生成的代码为:

javascript
'use strict';
exports.main = async (event, context) => {
	//event为客户端上传的参数
	console.log('event : ', event)
	
	//返回数据给客户端
	return event
}

客户端通过 uniCloud.callFunction 调用云函数:

javascript
uniCloud.callFunction({
  name: '云函数名(即文件名)',
  data: {/* 给云函数传的实参 */},
  success: (result) => {
    // result 是云函数 return 的返回值
  }
})

在云函数中调用 uniCloud.getPhoneNumber:

javascript
// 在云函数中获取
'use strict'
exports.main = async (event, context) => {
  // event里包含着客户端提交的参数
  const res = await uniCloud.getPhoneNumber({
    appid: context.APPID, // 替换成自己开通一键登录的应用的DCloud appid
    provider: 'univerify',
    apiKey: 'fc7aa3fb2672f947a501f2392a22501a', // 在开发者中心开通服务并获取apiKey
    apiSecret: 'b4d9fafeb3c3ed12ff6cc97b9f9a1817', // 在开发者中心开通服务并获取apiSecret
    access_token: event.access_token,
    openid: event.openid,
  })

  console.log(res) // res里包含手机号
  // 执行用户信息入库等操作,正常情况下不要把完整手机号返回给前端
  // 在云函数中也可以调用后端接口将手机号存入后端数据中或云数据库中
  return {
    code: 200,
    message: '获取手机号成功',
  }
}

云函数断点调试

调试云函数时,手机必须和电脑处于同一网络;在 HBuilderX 命令行窗口右上角开启断点调试,会提示安装插件。

云函数获取手机号结果

云函数还需要在 package.json 中添加依赖 uni-cloud-verify;获取手机号需要付费,充值后才能使用。

云函数成功获取手机号

易错点:手机没装电话卡拿不到运营商手机号;云函数未加依赖、未充值会失败;不要把完整手机号直接返回前端,应入库或交给后端处理。

2.3 实人认证:从开通到刷脸 ​

实人认证是通过视频方式验证用户身份的技术,uniCloud 提供了较简单的实现方式。本节把它接到神领物流中:用户填写姓名、身份证号,App 获取设备信息并调用云函数取得 certifyId,再调起刷脸 SDK 完成认证。

开通服务 ​

为什么需要:实人认证不是只写前端代码就能用,必须先开通云服务并配置服务空间,否则云函数拿不到认证能力。
怎么想:先按官方文档开通,再把当前服务空间绑定到实人认证。
怎么做:参见官方文档。转写中流程是:登录后台 → 服务空间 → 实人认证 → 同意协议 → 充值 → 安全配置中添加当前服务空间。
易错点:不充值或未绑定服务空间时,后续调用会失败;不要把它当成纯客户端 API。

配置神领物流 ​

为什么需要:App 端要使用实人认证模块,必须在工程里声明。
怎么想:到 manifest.json 的 App 模块设置中勾选“实人认证”。
怎么做:

配置神领物流实人认证模块

易错点:只开通后台、不勾选模块,真机运行时功能不可用。

新增实人认证页面与入口 ​

为什么需要:认证要贴合业务,需要独立页面收集姓名、身份证号。
怎么想:页面放在用户分包 subpkg_user,并在“我的”页面加入口;由于实人认证只支持 App,入口要用条件编译。
怎么做:

vue
<!-- subpkg_user/verify/index.vue -->
<script setup></script>
<template>
  <uni-forms class="verify-form">
    <uni-forms-item name="account">
      <input
        type="text"
        placeholder="请输入姓名"
        class="uni-input-input"
        placeholder-style="color: #818181"
      />
    </uni-forms-item>
    <uni-forms-item name="password">
      <input
        type="text"
        placeholder="请输入身份证号"
        class="uni-input-input"
        placeholder-style="color: #818181"
      />
    </uni-forms-item>
    <button class="submit-button">认证</button>
  </uni-forms>
</template>

<style lang="scss" scoped>
  .verify-form {
    padding: 120rpx 66rpx 66rpx;
  }

  .uni-forms-item {
    height: 100rpx;
    margin-bottom: 20 !important;
    border-bottom: 2rpx solid #eee;
    box-sizing: border-box;
  }

  ::v-deep .uni-forms-item__content {
    display: flex;
    align-items: center;
  }

  ::v-deep input {
    width: 100%;
    font-size: $uni-font-size-base;
    color: $uni-main-color;
  }

  ::v-deep .uni-forms-item__error {
    width: 100%;
    padding-top: 10rpx;
    border-top: 2rpx solid $uni-primary;
    color: $uni-primary;
    font-size: $uni-font-size-small;
    transition: none;
  }

  .submit-button {
    height: 100rpx;
    line-height: 100rpx;
    /* #ifdef APP */
    padding-top: 4rpx;
    /* #endif */
    margin-top: 80rpx;
    border: none;
    color: #fff;
    background-color: $uni-primary;
    border-radius: 100rpx;
    font-size: $uni-font-size-big;
  }

  button[disabled] {
    background-color: #fadcd9;
    color: #fff;
  }
</style>

pages.json 中配置到 subpkg_user:

json
{
  "pages": [],
  "globalStyle": {},
  "tabBar": {},
  "subPackages": [{
      "root": "subpkg_task",
      "pages": []
    },
    {
      "root": "subpkg_message",
      "pages": []
    },
    {
      "root": "subpkg_user",
      "pages": [
        {
          "path": "verify/index",
          "style": {
            "navigationBarTitleText": "实人认证"
          }
        }
      ]
    }
  ],
  "uniIdRouter": {}
}

“我的”页面加入口:

vue
<template>
  <view class="page-container">
    <view class="user-profile">...</view>
    <view class="month-overview">...</view>
    <view class="entry-list">
      <uni-list :border="false">
        <!-- #ifdef APP-PLUS -->
        <uni-list-item
          to="/subpkg_user/verify/index"
          showArrow
          title="实人认证"
        />
        <!-- #endif -->
      </uni-list>
    </view>
  </view>
</template>

易错点:页面建好后必须到 pages.json 配路径;H5、小程序不支持实人认证,链接要用 APP-PLUS 条件编译隐藏。

获取设备信息 ​

为什么需要:云函数获取 certifyId 时需要设备信息参与风控。
怎么想:用户点“认证”时先调 uni.getFacialRecognitionMetaInfo(),把返回值原样传给云函数。
怎么做:

vue
<script setup>
  function onFormSubmit() {
    // 1. 获取设备信息
    const metaInfo = uni.getFacialRecognitionMetaInfo()
    console.log(metaInfo)
  }
</script>
<template>
  <uni-forms class="verify-form">
    <button @click="onFormSubmit" class="submit-button">认证</button>
  </uni-forms>
</template>

易错点:metaInfo 不是可省略的展示数据;调试时可在控制台看到设备类型、系统版本、型号等信息。

创建云函数获取 certifyId ​

为什么需要:认证服务的 certifyId 必须由云函数向实人认证服务申请。
怎么想:在 cloudfunctions 下新建云函数,例如 uni-verify,并添加所需依赖;云函数接收 realName、idCard、metaInfo,返回认证结果。
怎么做:

新建云函数步骤一

新建云函数步骤二

新建云函数步骤三

javascript
'use strict'
exports.main = async (event, context) => {
  // 云函数获取实人认证实例
  const frvManager = uniCloud.getFacialRecognitionVerifyManager({
    requestId: context.requestId,
  })

  // 云函数提交姓名、身份证号以获取认证服务的certifyId
  const result = await frvManager.getCertifyId({
    realName: event.realName,
    idCard: event.idCard,
    metaInfo: event.metaInfo,
  })

  //返回数据给客户端
  return result
}

易错点:转写中强调云函数要右键“管理公共模块”添加依赖;依赖缺失或余额不足会报错。参数名必须与客户端传入一致。

客户端调用云函数并调起刷脸 ​

为什么需要:云函数返回 certifyId 后,App 才能调起摄像头刷脸。
怎么想:uniCloud.callFunction 传三个参数;成功回调里取 result.certifyId,再调 uni.startFacialRecognitionVerify。
怎么做:

vue
<script setup>
  import { ref } from 'vue'

  const realName = ref('用户姓名')
  const idCard = ref('用户身份证号')

  function onFormSubmit() {
    // 1. 获取设备信息
    const metaInfo = uni.getFacialRecognitionMetaInfo()
    // 2. 调用云函数
    uniCloud.callFunction({
      name: 'uni-verify',
      data: { metaInfo, realName: realName.value, idCard: idCard.value },
      success({ result }) {
        // 3. 客户端调起sdk刷脸认证
        uni.startFacialRecognitionVerify({
          certifyId: result.certifyId,
          success() {
            uni.utils.toast('认证成功!')
          },
          fail() {
            uni.utils.toast('认证失败!')
          },
        })
      },
      fail() {}
    })
  }
</script>
<template>
  <uni-forms class="verify-form">
    <button @click="onFormSubmit" class="submit-button">认证</button>
  </uni-forms>
</template>

易错点:ref 传参要写 .value;必须真机 App 调试,H5/小程序不支持;手机与电脑最好同一网络,转写中曾用手机热点解决调试连接问题;成功回调里注意 certifyId 的层级,按云函数实际返回结构取值。

代码示例 ​

示例一:登录页唤起一键登录(pages/login/index.vue) ​

vue
<!-- pages/login/index.vue -->
<script setup>
import { ref } from 'vue'

const tabIndex = ref(0)

// 切换登录类型
async function changeLoginType() {
  // #ifdef APP-PLUS
  // 仅 App 端:唤起运营商一键登录授权界面
  try {
    const { authResult } = await uni.login({
      provider: 'univerify',
      univerifyStyle: {
        fullScreen: true,
        icon: {
          path: 'static/logo.png' // 仅支持本地图片,默认显示 App logo
        },
        authButton: {
          normalColor: '#ef4f3f',    // 授权按钮正常背景色,默认 #3479f5
          highlightColor: '#ef4f3f'  // 按下背景色,仅 iOS 支持
        },
        privacyTerms: {
          defaultCheckBoxState: false,
          uncheckedImage: 'static/images/uncheckedImage.png',
          checkedImage: 'static/images/checkedImage.png',
          termsColor: '#ef4f3f'
        }
      }
    })

    console.log('authResult:', authResult)

    // 用户点了“同意授权”才会走到这里,此时只拿到 access_token / openid
    // 真正拿手机号必须交给云函数
    uniCloud.callFunction({
      name: 'uni-login',
      data: {
        access_token: authResult.access_token,
        openid: authResult.openid
      },
      success({ result }) {
        console.log('云函数返回:', result)
      },
      fail(err) {
        console.error('调用云函数失败:', err)
      }
    })
  } catch (err) {
    console.error('一键登录失败:', err)
  }
  // #endif

  // #ifndef APP-PLUS
  // 非 App 端不支持一键登录,退回账号密码 / 验证码登录
  tabIndex.value = Math.abs(tabIndex.value - 1)
  // #endif
}
</script>

<template>
  <view class="login-page">
    <view class="login-type" @click="changeLoginType">
      切换登录方式(当前:{{ tabIndex === 0 ? '密码登录' : '验证码登录' }})
    </view>
  </view>
</template>

<style lang="scss" scoped>
.login-page {
  padding: 120rpx 66rpx;
}
.login-type {
  color: #ef4f3f;
  font-size: 28rpx;
}
</style>

要点:

  • provider: 'univerify' 指定一键登录服务,univerifyStyle 只能有限定制(logo、按钮色、隐私协议勾选框等)。
  • 条件编译 // #ifdef APP-PLUS 包住 uni.login,H5/小程序分支只切 tab,否则非 App 端会直接报错。
  • uni.login 只完成“授权”,返回的 authResult 里是 access_token 和 openid,不是手机号,必须再交给云函数。
  • 易错点:一键登录必须先在开发者中心开通服务并重新打包(自定义基座或正式包),标准基座调不起来;手机需插卡,无卡拿不到号码。

示例二:云函数换取手机号(cloudfunctions/uni-login/index.js + package.json) ​

js
// cloudfunctions/uni-login/index.js
'use strict'

exports.main = async (event, context) => {
  // event 里是客户端 uniCloud.callFunction 传上来的 access_token / openid
  const res = await uniCloud.getPhoneNumber({
    appid: context.APPID, // 也可写死自己开通一键登录的应用的 DCloud appid
    provider: 'univerify',
    apiKey: 'fc7aa3fb2672f947a501f2392a22501a',      // 开发者中心开通服务后获取
    apiSecret: 'b4d9fafeb3c3ed12ff6cc97b9f9a1817',   // 开发者中心开通服务后获取
    access_token: event.access_token,
    openid: event.openid
  })

  console.log('getPhoneNumber result:', res) // res 里包含手机号

  // 正常情况下不要把完整手机号返回给前端,
  // 应在云函数里调后端接口入库 / 换 token
  return {
    code: 200,
    message: '获取手机号成功',
    phoneNumber: res.phoneNumber
  }
}
json
// cloudfunctions/uni-login/package.json
{
  "name": "uni-login",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "uni-cloud-verify": {}
  }
}

要点:

  • context.APPID 会自动读到当前应用 ID,比写死更省事;apiKey/apiSecret 来自开发者中心开通服务页面。
  • access_token 和 openid 只能通过 event 从客户端带进来,是 authResult 的直接传递。
  • 易错点一:必须给云函数加 uni-cloud-verify 依赖,否则调用会报“缺少扩展库”。
  • 易错点二:该接口按次计费,未充值会提示余额不足,代码没问题也调不通。
  • 易错点三:App 端调试云函数要求手机和电脑在同一网络下(同 WiFi 或手机开热点给电脑)。

示例三:实人认证页面 + 云函数(subpkg_user/verify/index.vue + uni-verify) ​

vue
<!-- subpkg_user/verify/index.vue -->
<script setup>
import { ref } from 'vue'

const realName = ref('')
const idCard = ref('')

function onFormSubmit() {
  // 1. 获取设备信息
  const metaInfo = uni.getFacialRecognitionMetaInfo()
  console.log('metaInfo:', metaInfo)

  // 2. 调用云函数,用姓名 + 身份证号 + 设备信息换 certifyId
  uniCloud.callFunction({
    name: 'uni-verify',
    data: {
      metaInfo,
      realName: realName.value,
      idCard: idCard.value
    },
    success({ result }) {
      console.log('云函数返回:', result)

      // 3. 客户端调起 SDK 刷脸认证
      uni.startFacialRecognitionVerify({
        certifyId: result.certifyId,
        success() {
          uni.showToast({ title: '认证成功', icon: 'none' })
        },
        fail() {
          uni.showToast({ title: '认证失败', icon: 'none' })
        }
      })
    },
    fail(err) {
      console.error('调用云函数失败:', err)
    }
  })
}
</script>

<template>
  <uni-forms class="verify-form">
    <uni-forms-item name="realName">
      <input
        v-model="realName"
        type="text"
        placeholder="请输入姓名"
        class="uni-input-input"
        placeholder-style="color: #818181"
      />
    </uni-forms-item>
    <uni-forms-item name="idCard">
      <input
        v-model="idCard"
        type="text"
        placeholder="请输入身份证号"
        class="uni-input-input"
        placeholder-style="color: #818181"
      />
    </uni-forms-item>
    <button class="submit-button" @click="onFormSubmit">认证</button>
  </uni-forms>
</template>

<style lang="scss" scoped>
.verify-form {
  padding: 120rpx 66rpx 66rpx;
}

.uni-forms-item {
  height: 100rpx;
  margin-bottom: 20 !important;
  border-bottom: 2rpx solid #eee;
  box-sizing: border-box;
}

::v-deep .uni-forms-item__content {
  display: flex;
  align-items: center;
}

::v-deep input {
  width: 100%;
  font-size: $uni-font-size-base;
  color: $uni-main-color;
}

.submit-button {
  height: 100rpx;
  line-height: 100rpx;
  /* #ifdef APP */
  padding-top: 4rpx;
  /* #endif */
  margin-top: 80rpx;
  border: none;
  color: #fff;
  background-color: $uni-primary;
  border-radius: 100rpx;
  font-size: $uni-font-size-big;
}
</style>
js
// cloudfunctions/uni-verify/index.js
'use strict'

exports.main = async (event, context) => {
  // 云函数获取实人认证实例
  const frvManager = uniCloud.getFacialRecognitionVerifyManager({
    requestId: context.requestId
  })

  // 提交姓名、身份证号、设备信息,换取认证服务的 certifyId
  const result = await frvManager.getCertifyId({
    realName: event.realName,
    idCard: event.idCard,
    metaInfo: event.metaInfo
  })

  return result
}

要点:

  • 三步固定流程:uni.getFacialRecognitionMetaInfo() 拿设备信息 → 云函数换 certifyId → uni.startFacialRecognitionVerify({ certifyId }) 调起摄像头刷脸。
  • 云函数必须先“管理公共模块”添加 uni-cloud-verify 依赖,否则 getFacialRecognitionVerifyManager 取不到。
  • 页面必须到 pages.json 的 subpkg_user 分包里注册 "path": "verify/index",否则跳转打不开;入口链接在“我的”页用 <!-- #ifdef APP-PLUS --> 包裹,因为实人认证仅 App 端支持。
  • 易错点:success({ result }) 拿到的结构可能多包一层,若 result.certifyId 取到 undefined,先 console.log(result) 看实际层级再取值。
  • 易错点:姓名、身份证号一定要用 v-model 绑定后传值,写死字符串只会认证固定的人。

常见错误 ​

  1. 现象:H5 端点击"切换登录方式"没反应,登录方式不切换。 原因:把 uni.login({provider:'univerify'}) 写在了所有平台都会执行的位置,H5 端调用直接失败,后面的切换逻辑也没执行。 修法:用条件编译拆开逻辑——#ifdef APP-PLUS 里走 uni.login,#ifndef APP-PLUS 里执行 tabIndex.value = Math.abs(tabIndex.value - 1)。

  2. 现象:授权框弹出、用户点了同意,但拿不到手机号,只有 access_token 和 openid。 原因:把"授权"当成"取号"。授权界面只是征得用户同意,真实号码必须由运营商接口返回,而该接口只能在服务端或云函数中调用。 修法:客户端把 authResult(含 access_token、openid)传给云函数,在云函数内调 uniCloud.getPhoneNumber 取号。

  3. 现象:云函数报错"缺少扩展库 uni-cloud-verify",或提示余额不足。 原因:云函数的 package.json 没声明依赖;同时该接口是付费能力,账户未充值。 修法:在云函数目录的 package.json 中补 "dependencies": { "uni-cloud-verify": {} }(或用"管理公共模块"添加),并到开发者中心充值后再调用。

  4. 现象:云函数里的 console.log 在本地调试窗口看不到输出,断点也进不去。 原因:App 端调试云函数要求手机与电脑处于同一网段(教室/热点隔离网络最容易出现);另外首次使用需安装断点调试插件。 修法:让手机开热点给电脑连(或同连一个 Wi-Fi),点调试窗口右上角【开启断点调试】按提示装插件,再重启 HBuilderX 重新运行。

  5. 现象:云函数跑通了,但登录接口报"手机号为空"或前端拿到的字段和预期对不上。 原因:uni.login 的成功回调结构是 { authResult: { access_token, openid } },云函数返回值又多包了一层 result,取字段时层级写错。 修法:前端 const { authResult } = await uni.login(...);callFunction 成功回调写 success({ result }),再按 result.certifyId / result.phoneNumber 取实际字段,先 console.log 打印确认层级再写业务。

  6. 现象:云函数把完整手机号 return 给了前端,被抓包就能看到。 原因:图省事把取号结果原样返回,忽略了敏感信息边界。 修法:云函数内直接把手机号交后端入库/换 token,只向前端返回 { code, message } 或登录凭证。

  7. 现象:H5 和小程序里也出现了"实人认证"入口,点进去页面空白或调 uni.startFacialRecognitionVerify 报"不支持"。 原因:实人认证仅 App 端可用,入口没做平台限制。 修法:模板中把 <uni-list-item to="/subpkg_user/verify/index" ... /> 用 #ifdef APP-PLUS 包起来;页面跳转和 API 调用同样加条件编译。

  8. 现象:新建的 subpkg_user/verify/index.vue 能编译,但跳转 404。 原因:手工建的文件没有在 pages.json 注册,分包页必须在 subPackages 对应 root 下补 path。 修法:在 subpkg_user 的 pages 数组里补 { "path": "verify/index", "style": { "navigationBarTitleText": "实人认证" } }。

  9. 现象:点"认证"后摄像头不弹,或者弹了但立刻失败。 原因:用户没填姓名/身份证号就提交,或手机未插 SIM 卡、一键登录/实人认证未在 manifest 勾选模块、未重新打自定义基座。 修法:提交前做表单校验;确认 manifest.json 已勾选对应模块并重新打包自定义基座;一键登录场景确认设备插卡。


练习 ​

  1. 平台分支改造:把登录页的"一键登录"改成——App 端点按钮直接唤起授权,H5/小程序端点按钮切到账号密码登录。写完后分别在 H5 和真机上验证,并口头说明为什么 H5 走不通。

  2. 取号安全改造:把现有云函数改为不向前端返回手机号,而是"取号 → 调后端登录接口 → 返回 token"。要求前端只拿到 token,并用抓包或 console.log 证明前端看不到号码。

  3. 实人认证表单校验:给 subpkg_user/verify/index.vue 补上校验——姓名非空、身份证号 18 位,不通过时用 uni.showToast 提示且不发起云函数调用;通过后再走"设备信息 → 云函数 → 刷脸"三步。

  4. 错误定位:给出下面这段代码,请指出至少 3 处问题并改正。

js
uniCloud.callFunction({
  name: 'uni-verify',
  data: { realName, idCard },
  success(res) {
    uni.startFacialRecognitionVerify({ certifyId: res.result })
  }
})

(提示:metaInfo 缺失、certifyId 层级、success/fail 分支与提示、页面/入口的平台限制。)

  1. 入口兼容:在"我的"页面加"实人认证"入口,要求 App 端可见可跳转、H5 端完全不可见。提交后请截图对比两端效果,并说明用到了哪种条件编译指令(模板级还是 JS 级)。

原视频对照 ​

#视频时长
1一键登录-用户授权12:15
2一键登录-云函数获取手机号16:49
3实人认证-功能介绍10:43
4实人认证-开通服务10:40
5实人认证-云函数23:05
6实人认证-摄像头09:20

小结 ​

  • 一键登录 = 授权 + 取号两段。前端只负责唤起授权界面拿到 access_token/openid,真实号码必须由云函数或服务端去运营商换取,二者不能混为一谈。
  • 云函数就是跑在服务器上的普通函数:右键新建 → 写逻辑 → uniCloud.callFunction({ name, data, success }) 调用;name 是函数名,data 是实参,success 里拿返回值。
  • 云函数返回值先打印再取值。多层嵌套({ result: { result: {...} } })是常态,先 console.log 确认层级,再写业务字段,能省掉大量"取不到值"的排查时间。
  • 敏感信息不出服务端。手机号、身份证号在云函数内入库或换 token,前端只拿业务结果,这是习惯而非可选项。
  • 实人认证是固定三步:uni.getFacialRecognitionMetaInfo() 取设备信息 → 云函数拿 certifyId → uni.startFacialRecognitionVerify({ certifyId }) 起摄像头;流程固定,记住套路即可复用。
  • App 专有能力必须条件编译 + 真机 + 同网段。H5/小程序跑不通不是代码错,是平台不支持;调试卡住先查网络和基座,再查代码。
  • 云服务三件套别漏:开发者中心开通并充值 → manifest.json 勾选模块 → 重新打自定义基座。少任何一步,代码再对也报错。
  • 新建页面必注册,分包页要去 subPackages 对应 root 下补 path,否则编译通过但跳转 404。