uniapp 实现用户手机号授权登录完整流程
要实现 uniapp 的用户手机号授权登录,首先需要明确:uniapp 中手机号授权登录分「小程序端」和「App 端」,实现方案差异较大,其中小程序端依赖微信开放平台能力,App 端多采用短信验证码方案,下面分别详细讲解完整可落地的流程。
一、 小程序端(以微信小程序为例):基于微信手机号快速授权
核心前提(必须满足):
-
已完成微信小程序注册,且小程序已通过微信平台认证(个人主体小程序不支持手机号授权功能,仅企业 / 个体工商户等认证主体可用);
-
已在 uniapp 项目中配置小程序 AppID:打开 manifest.json→ 「微信小程序配置」→ 填写正确的appid,并确保项目已关联对应的微信小程序; -
小程序已开通「手机号快速填写」权限(默认开通,可在微信公众平台「功能」→「接口设置」中确认)。
整体流程概述
用户触发登录 → 获取微信登录临时凭证code → 后端用code换取session_key和openid → 前端获取手机号授权凭证iv和encryptedData → 前端将iv、encryptedData、code传给后端 → 后端解密获取手机号并完成登录 / 注册 → 返回用户登录态给前端。
步骤 1:前端实现(uniapp 代码)
关键点说明
-
微信小程序的手机号授权必须通过 button组件的open-type="getPhoneNumber"触发,无法通过 API 主动调用,这是微信的强制规范; -
需先调用 uni.login()获取临时登录凭证code,用于后端换取微信开放平台的session_key(解密手机号必须用到session_key); -
授权结果通过 @getphonenumber事件回调获取,包含加密的手机号数据和授权状态。
<template><viewclass="login-container"><!-- 手机号授权登录按钮(必须设置open-type="getPhoneNumber") --><buttonopen-type="getPhoneNumber"@getphonenumber="handleGetPhoneNumber"class="login-btn">微信快捷登录(获取手机号)</button></view></template><script>export default {methods: {/*** 处理手机号授权回调* @param {Object} e 授权结果回调参数*/async handleGetPhoneNumber(e) {try {// 1. 判断用户是否允许授权if (e.detail.errMsg !== "getPhoneNumber:ok") {uni.showToast({title: "你已拒绝授权手机号,无法完成登录",icon: "none"});return;}// 2. 获取微信登录临时凭证code(用于后端换取session_key)const loginRes = await uni.login({provider: "weixin" // 明确指定微信登录(uniapp多端兼容时需指定)});if (loginRes.errMsg !== "login:ok") {uni.showToast({title: "获取登录凭证失败",icon: "none"});return;}const code = loginRes.code;// 3. 提取手机号加密数据(iv:加密向量,encryptedData:加密手机号数据)const { iv, encryptedData } = e.detail;// 4. 前端将code、iv、encryptedData传给后端(核心:不做本地解密,安全交给后端)const res = await uni.request({url: "https://你的后端域名/api/wechat/phone/login", // 替换为你的后端接口method: "POST",data: {code,iv,encryptedData}});// 5. 处理后端返回结果if (res.data.code === 200) {// 登录成功:保存登录态(token/userInfo)到本地,跳转首页uni.setStorageSync("token", res.data.data.token);uni.setStorageSync("userInfo", res.data.data.userInfo);uni.showToast({ title: "登录成功", icon: "success" });setTimeout(() => {uni.switchTab({ url: "/pages/index/index" });}, 1500);} else {uni.showToast({title: res.data.msg || "登录失败,请重试",icon: "none"});}} catch (error) {console.error("手机号登录异常:", error);uni.showToast({ title: "网络异常,请稍后重试", icon: "none" });}}}};</script><stylescoped>.login-container {display: flex;justify-content: center;align-items: center;height: 100vh;}.login-btn {width: 80%;height: 88rpx;line-height: 88rpx;background-color: #07c160;color: #fff;border-radius: 44rpx;font-size: 32rpx;}</style>
-
微信开放平台提供的解密算法,推荐使用官方封装的 SDK(如 Java: weixin-java-miniapp,Node.js:wechat-miniprogram); -
后端需先通过 code调用微信口https://api.weixin.qq.com/sns/jscode2session,换取session_key(会话密钥)和openid(用户唯一标识)。
关键步骤(以 Node.js 为例,Express 框架)
1.安装依赖:
npm install wechat-miniprogram crypto-js
const express = require("express");const router = express.Router();const request = require("request-promise");const CryptoJS = require("crypto-js");// 微信小程序配置(替换为你的小程序AppID和AppSecret)const WX_CONFIG = {appid: "你的小程序AppID",secret: "你的小程序AppSecret",jscode2sessionUrl: "https://api.weixin.qq.com/sns/jscode2session"};/*** 微信手机号解密工具函数* @param {String} encryptedData 前端传递的加密手机号数据* @param {String} iv 前端传递的加密向量* @param {String} sessionKey 微信返回的会话密钥* @returns {Object} 解密后的用户数据(包含手机号)*/functiondecryptPhoneNumber(encryptedData, iv, sessionKey) {// 解码base64数据const sessionKeyBuf = CryptoJS.enc.Base64.parse(sessionKey);const encryptedDataBuf = CryptoJS.enc.Base64.parse(encryptedData);const ivBuf = CryptoJS.enc.Base64.parse(iv);// AES-128-CBC解密(微信指定算法)const decrypt = CryptoJS.AES.decrypt({ ciphertext: encryptedDataBuf },sessionKeyBuf,{iv: ivBuf,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});// 转换为JSON对象并返回const result = JSON.parse(CryptoJS.enc.Utf8.stringify(decrypt));return result;}// 手机号登录接口router.post("/wechat/phone/login", async (req, res) => {try {const { code, iv, encryptedData } = req.body;// 1. 校验参数是否完整if(!code || !iv || !encryptedData) {return res.json({ code: 400, msg: "参数不全,请重试" });}// 2. 调用微信接口,换取session_key和openidconst wxRes = await request({url: WX_CONFIG.jscode2sessionUrl,method: "GET",qs: {appid: WX_CONFIG.appid,secret: WX_CONFIG.secret,js_code: code,grant_type: "authorization_code"},json: true});// 3. 校验微信接口返回结果(是否获取到session_key)if(wxRes.errcode) {return res.json({ code: 500, msg: "获取微信会话密钥失败" });}const { session_key, openid } = wxRes;// 4. 解密手机号const phoneData = decryptPhoneNumber(encryptedData, iv, session_key);if(!phoneData.phoneNumber) {return res.json({ code: 500, msg: "解密手机号失败" });}const phoneNumber = phoneData.phoneNumber;// 5. 业务逻辑:查询用户是否存在(不存在则注册,存在则更新登录信息)// 此处为模拟逻辑,实际需对接数据库let userInfo = await db.query("SELECT * FROM user WHERE phone = ?", [phoneNumber]);if(!userInfo.length) {// 新用户:注册await db.query("INSERT INTO user (phone, openid, create_time) VALUES (?, ?, ?)", [phoneNumber,openid,new Date()]);userInfo = await db.query("SELECT * FROM user WHERE phone = ?", [phoneNumber]);} else {// 老用户:更新最后登录时间await db.query("UPDATE user SET last_login_time = ? WHERE id = ?", [new Date(),userInfo[0].id]);}// 6. 生成登录token(此处为模拟,实际需用jwt等工具生成)const token = "xxx_" + userInfo[0].id + "_" + Date.now();// 7. 返回登录结果给前端res.json({code: 200,msg: "登录成功",data: {token,userInfo: {id: userInfo[0].id,phone: userInfo[0].phone,nickname: userInfo[0].nickname || "微信用户"}}});} catch(error) {console.error("手机号登录后端异常:", error);res.json({ code: 500, msg: "服务器内部错误" });}});module.exports = router;
-
本地调试:使用 HBuilderX 运行项目到「微信开发者工具」,确保微信开发者工具已登录对应小程序账号,且「不校验合法域名」(调试模式); -
上线打包:在 HBuilderX 中点击「发行」→「微信小程序」,生成 wxapkg包,上传到微信公众平台进行审核发布; -
上线后需配置「合法 request 域名」(微信公众平台「开发」→「开发设置」→「服务器域名」),否则无法调用后端接口。
二、 App 端:手机号登录(短信验证码方案)
核心说明
App 端无法直接获取手机运营商的手机号授权,主流方案是「手机号 + 短信验证码」登录,uniapp 提供uni.request()实现接口请求,uni.login()可获取设备唯一标识(可选),流程更简单通用。
完整流程
用户输入手机号 → 点击获取验证码 → 前端校验手机号格式 → 后端发送短信验证码(对接短信服务商:阿里云 / 腾讯云短信) → 用户输入验证码 → 前端提交「手机号 + 验证码」 → 后端校验验证码有效性 → 完成登录 / 注册 → 返回登录态。

前端核心代码示例
<template><viewclass="app-login-container"><viewclass="input-item"><inputtype="number"v-model="phoneNumber"placeholder="请输入手机号" /></view><viewclass="input-item"><inputtype="number"v-model="verifyCode"placeholder="请输入6位验证码" /><buttonclass="get-code-btn" @click="getVerifyCode":disabled="countDown > 0">{{ countDown > 0 ? `${countDown}s后重新获取` : "获取验证码" }}</button></view><buttonclass="submit-btn" @click="handleSubmitLogin">登录</button></view></template><script>export default {data() {return {phoneNumber: "", // 用户输入的手机号verifyCode: "", // 用户输入的验证码countDown: 0 // 验证码倒计时};},methods: {/*** 校验手机号格式*/checkPhoneFormat() {const reg = /^1[3-9]\d{9}$/;if (!reg.test(this.phoneNumber)) {uni.showToast({ title: "请输入正确的11位手机号", icon: "none" });return false;}return true;},/*** 获取短信验证码*/async getVerifyCode() {if (!this.checkPhoneFormat()) return;// 调用后端发送验证码接口const res = await uni.request({url: "https://你的后端域名/api/app/send/verify/code",method: "POST",data: { phone: this.phoneNumber }});if (res.data.code === 200) {uni.showToast({ title: "验证码发送成功,请注意查收", icon: "success" });// 启动倒计时(60s)this.countDown = 60;const timer = setInterval(() => {this.countDown--;if (this.countDown <= 0) {clearInterval(timer);}}, 1000);} else {uni.showToast({ title: res.data.msg || "验证码发送失败", icon: "none" });}},/*** 提交登录*/async handleSubmitLogin() {if (!this.checkPhoneFormat()) return;if (!this.verifyCode || this.verifyCode.length !== 6) {uni.showToast({ title: "请输入正确的6位验证码", icon: "none" });return;}// 提交手机号+验证码到后端const res = await uni.request({url: "https://你的后端域名/api/app/phone/login",method: "POST",data: {phone: this.phoneNumber,verifyCode: this.verifyCode}});if (res.data.code === 200) {// 登录成功:保存登录态,跳转首页uni.setStorageSync("token", res.data.data.token);uni.showToast({ title: "登录成功", icon: "success" });setTimeout(() => {uni.switchTab({ url: "/pages/index/index" });}, 1500);} else {uni.showToast({ title: res.data.msg || "登录失败", icon: "none" });}}}};</script>
-
安全性:手机号解密必须在后端完成,严禁将session_key暴露到前端,也不要在前端进行解密(session_key泄露会导致用户数据被篡改); -
小程序主体限制:个人主体小程序无法使用手机号授权功能,必须进行企业 / 个体工商户认证; -
session_key有效期:session_key是临时会话密钥,会随用户退出登录、会话过期失效,需重新通过code换取; -
App 端短信验证码:需对接正规短信服务商,且添加图形验证码 / 滑块验证(防止恶意刷短信); -
登录态保存:推荐将后端返回的 token保存到uni.setStorageSync(),后续接口请求通过header携带token进行身份校验; -
多端兼容:如果项目需要同时兼容小程序和 App,可通过 uni.getSystemInfoSync().platform判断运行环境,分别执行对应登录逻辑。

-
小程序端手机号授权依赖「微信开放平台」,核心是 button组件open-type="getPhoneNumber"+ 后端解密encryptedData; -
App 端主流是「手机号 + 短信验证码」方案,无平台特殊限制,通用性更强; -
核心流程共性:前端收集 / 传递关键数据 → 后端处理业务逻辑 + 数据校验 → 返回登录态 → 前端保存并跳转; -
安全性是核心,敏感操作(解密、验证码校验、token 生成)必须放在后端。
夜雨聆风
