在 UniApp 中接入 CloudBase 用户认证
一句话定义:用
@cloudbase/adapter-uni-app给@cloudbase/js-sdk注册 UniApp 适配器,然后用auth.signInWithSms/ 微信一键登录拿身份,代码一份跑 H5 / 微信小程序 / 支付宝小程序 / 抖音小程序 / iOS / Android 六端,登录态共享。预计耗时:50 分钟 | 难度:进阶
适用场景
- 适用:团队是 UniApp(Vue3 + Composition API)跨端,想要一份代码同时出 H5、微信小程序、支付宝小程序、抖音小程序、iOS、Android
- 适用:不 想为每端重写一套认证,要一套 user 表 + 一套 uid
- 适用:已经在用 add-auth-wechat-miniprogram 的纯小程序方案,现在要扩到多端
- 不适用:纯微信小程序专属业务直接用
wx.cloud,不必上 UniApp 这一层 - 不适用:Flutter / React Native / 鸿蒙原生场景,UniApp 适配器不覆盖,各自有专门 SDK
- 不适用:UniApp x(纯原生编译模式),目前
@cloudbase/adapter-uni-app只支持 UniApp 标准模式
环境要求
| 依赖 | 版本 |
|---|---|
| HBuilderX | 最新稳定版(3.99 及以上) |
| Node.js | ≥ 18 |
@cloudbase/js-sdk | 2.27.3 |
@cloudbase/adapter-uni-app | 1.5.0 |
| Vue | ^3.4.x(Composition API) |
| 编译器 | @vue/compiler-sfc ≥ 3.4,UniApp vite 模板自带 |
另外需要:
- 已开通的 CloudBase 环境 ID,地域选「上海」(短信验证码登录仅支持上海地域)
- 控制台 → 身份认证 → 登录方式,把要用的几种方式开启:
- 「短信验证码登录」(H5 / 支付宝小程序 / 抖音小程序 / App 共用)
- 「微信小程序登录」(在微信小程序端做一键登录用)
- App 端额外要在 环境配置 → 安全来源 → 移动应用安全来源 添加应用,拿
appSign+appAccessKey
短信登录仅支持上海地域。CloudBase init 时不传 region 默认就是上海;广州 / 北京环境用不了短信。
第一步:HBuilderX 创建 Vue3 项目,装 SDK
HBuilderX 顶部菜单 文件 → 新建 → 项目 → uni-app,模板选 Vue3 / Vite 版(默认勾选 Composition API)。
在项目根目录装 SDK 和适配器:
npm install @cloudbase/adapter-uni-app @cloudbase/js-sdk
UniApp Vue3 模板默认就有 package.json 和多端编译命令,不用额外配 vite。
第二步:初始化 cloudbase + 注册 adapter
关键:useAdapters 必须在 cloudbase.init 之前调用,顺序错了适配器不生效,SDK 会按浏览器环境跑,小程序端会全线报错。
src/utils/cloudbase.ts:
import cloudbase from '@cloudbase/js-sdk';
import adapter from '@cloudbase/adapter-uni-app';
// 1. 注册 UniApp 适配器,必须传 uni 对象(图形验证码弹窗要用)
const options = {
uni: uni,
};
cloudbase.useAdapters(adapter, options);
// 2. 初始化 SDK
export const app = cloudbase.init({
env: 'your-env-id',
// 仅 App 端(iOS / Android)需要这两项
// H5 / 各家小程序端可以不传或留空
appSign: 'your-app-sign',
appSecret: {
appAccessKeyId: 1,
appAccessKey: 'your-app-access-key',
},
});
export const auth = app.auth({
persistence: 'local', // 登录态本地长存,显式 signOut 才清
});
要点:
uni对象在 UniApp 里是全局可用的,不用 import,直接传给options.uni- App 端缺
appSign/appSecret会在调云函数 / 数据库时报「app sign invalid」 persistence: 'local'在每个端都映射到该端的持久化存储(H5 → localStorage、微信小程序 → wx.setStorage、支付宝 → my.setStorage、抖音 → tt.setStorage、App → uni.setStorage),业务不用关心底层
第三步:微信小程序端配合法域名 + 一键登录
3.1 微信公众平台配置合法域名
微信小程序后台 → 开发 → 开发管理 → 开发设置 → 服务器域名,把以下域名加入白名单(your-env-id 替换成你的环境 ID):
request 合法域名:
https://tcb-api.tencentcloudapi.com
https://your-env-id.service.tcloudbase.com
uploadFile 合法域名:
https://cos.ap-shanghai.myqcloud.com
downloadFile 合法域名:
https://your-env-id.tcb.qcloud.la
https://cos.ap-shanghai.myqcloud.com
域名按你 CloudBase 环境实际所在地域调整(广州环境是 ap-guangzhou,北京环境是 ap-beijing)。
3.2 微信小程序端用 uni.login 拿 code 走自定义登录
UniApp 跨端:微信小程序端用 uni.login({ provider: 'weixin' }) 拿 code,前端把 code 传给云函数,云函数侧用 wx.openApi.auth.code2Session 换 openid,再用 CloudBase Auth Open API 给前端发自定义 ticket,前端 auth.signInWithCustomTicket(ticket) 完成登录。
src/pages/login/login.vue 一键登录入口:
import { auth } from '@/utils/cloudbase';
async function loginByWechatMP() {
// 1. 拿 code
const { code } = await uni.login({ provider: 'weixin' });
// 2. 调云函数换 ticket(云函数实现见下方)
const res = await uni.request({
url: 'https://your-env-id.service.tcloudbase.com/wxLogin',
method: 'POST',
data: { code },
});
const ticket = (res.data as any).ticket;
// 3. 用 ticket 登录
await auth.signInWithCustomTicket(ticket);
uni.reLaunch({ url: '/pages/index/index' });
}
云函数 wxLogin 的实现思路和 connect-openai-api-cloud-function 同一套(云函数侧持密钥),关键代码:
// 云函数:用 wxAppId + wxAppSecret 换 openid,再签自定义 ticket
const cloud = require('wx-server-sdk');
cloud.init();
exports.main = async (event) => {
const { code } = event;
const { openid } = await cloud.openapi({
appid: 'your-mp-appid',
}).auth.code2Session({ code });
const auth = cloud.auth();
const ticket = await auth.createTicket(openid, { refresh: 7200 * 1000 });
return { ticket };
};
微信小程序端不用走短信验证码,一键登录体验最好。
第四步:H5 / 支付宝 / 抖音端短信验证码登录
非微信小程序端走 v2 短信登录,两步:getVerification 拿 verificationInfo,signInWithSms 用验证码登录。
src/pages/login/login-sms.vue:
<template>
<view class="login">
<input
v-model="phone"
placeholder="+86 13800000000"
class="input"
/>
<button :disabled="countdown > 0" @click="sendCode">
{{ countdown > 0 ? `${countdown}s 后重发` : '获取验证码' }}
</button>
<input v-model="code" placeholder="6 位验证码" class="input" />
<button type="primary" @click="submit">登录</button>
<text v-if="err" class="err">{{ err }}</text>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { auth } from '@/utils/cloudbase';
const phone = ref('');
const code = ref('');
const err = ref('');
const countdown = ref(0);
let verificationInfo: any = null;
async function sendCode() {
err.value = '';
if (!/^\+86\s?\d{11}$/.test(phone.value)) {
err.value = '手机号格式应为 +86 13800000000';
return;
}
try {
verificationInfo = await auth.getVerification({
phone_number: phone.value,
});
countdown.value = 60;
const t = setInterval(() => {
if (countdown.value <= 1) {
clearInterval(t);
countdown.value = 0;
return;
}
countdown.value -= 1;
}, 1000);
} catch (e: any) {
err.value = e.message || '发送失败';
}
}
async function submit() {
err.value = '';
try {
await auth.signInWithSms({
verificationInfo: { verification_id: verificationInfo.verification_id },
verificationCode: code.value,
phoneNum: phone.value,
});
uni.reLaunch({ url: '/pages/index/index' });
} catch (e: any) {
err.value = e.message || '登录失败';
}
}
</script>
<style>
.input { border: 1rpx solid #ccc; padding: 16rpx; margin-bottom: 16rpx; }
.err { color: red; margin-top: 16rpx; display: block; }
</style>
要点:
- 手机号必须带
+86区号,SDK 接口校验严格 getVerification返回的verification_id必须包在verificationInfo里传给signInWithSms,不能直接传字符串- 同号 30 秒内只能发 1 条,日上限默认 30 条/天,在控制台 身份认证 → 登录方式 → 短信验证码 调
- 控制台开启「短信登录」时如果勾选了「图形验证码」,前端要监听
uni.$on('CAPTCHA_DATA_CHANGE', ...)把图形验证码图片渲染出来,用户输入后通过uni.$emit('RESOLVE_CAPTCHA_DATA', { captcha_token, expires_in })回传,适配器才能继续走流程(适配器内部用事件总线,详见 UniApp adapter 文档)
第五步:App 端配 appSign + appSecret
App 端(iOS / Android)和小程序、H5 不一样,要在 CloudBase 控制台单独注册「应用安全来源」拿凭证。
控制台 → 环境配置 → 安全来源 → 移动应用安全来源 → 添加应用:
- 应用名:随便起,比如
my-uniapp-ios - 应用包名(iOS 是 Bundle ID,Android 是 package name):和
manifest.json里 App 模块配置 → AppID 对应的真机包名一致 - 平台:iOS / Android 各加一条
提交后控制台会给:
appSign:对应your-app-sign(应用标识)appSecret.appAccessKey:对应your-app-access-key(应用凭证)appSecret.appAccessKeyId:版本号,默认1
把这三个值填到 cloudbase.init 里:
cloudbase.init({
env: 'your-env-id',
appSign: 'my-uniapp-ios-sign',
appSecret: {
appAccessKeyId: 1,
appAccessKey: 'xxxxxx',
},
});
App 端短信登录用同一份代码,直接复用第四步的 login-sms.vue,不用改。
运行验证
不同端用不同命令编译,分别跑一遍:
# H5(浏览器)
npm run dev:h5
# 微信小程序(然后用微信开发者工具打开 dist/dev/mp-weixin)
npm run dev:mp-weixin
# 支付宝小程序(用支付宝小程序 IDE 打开 dist/dev/mp-alipay)
npm run dev:mp-alipay
# 抖音小程序(用抖音开发者工具打开 dist/dev/mp-toutiao)
npm run dev:mp-toutiao
App 端在 HBuilderX 顶部菜单 运行 → 运行到手机或模拟器,选真机或 iOS 模拟器。
每端验收 checklist:
- H5:用真实手机号走短信登录,Console 不应有错;成功后跳到首页,刷新页面登录态还在
- 微信小程序:点「微信一键登录」,授权后直接进首页,控制台 → 身份认证 → 用户管理 能看到
loginType是WECHAT_OPEN_ACCESS_TOKEN(自定义登录)的用户 - 支付宝 / 抖音小程序:走短信登录,登录成功后能在用户管理里看到对应记录
- App:同手机号在 H5 端登录、再在 App 端登录,两端能拿到同一个
uid(说明 user 表共享) - 退出登录:
auth.signOut()调一次,auth.hasLoginState()返回false,刷新页面也确实没登录态
常见错误
| 错误码 / 现象 | 原因 | 修复 |
|---|---|---|
| 全端 init 后所有调用报「unknown environment」/「sdk not init」 | cloudbase.useAdapters(adapter, options) 没在 cloudbase.init 之前调用 |