乐于分享
好东西不私藏

uniapp 实现用户手机号授权登录完整流程

uniapp 实现用户手机号授权登录完整流程

要实现 uniapp 的用户手机号授权登录,首先需要明确:uniapp 中手机号授权登录分「小程序端」和「App 端」,实现方案差异较大,其中小程序端依赖微信开放平台能力,App 端多采用短信验证码方案,下面分别详细讲解完整可落地的流程。

一、 小程序端(以微信小程序为例):基于微信手机号快速授权

核心前提(必须满足):

  1. 已完成微信小程序注册,且小程序已通过微信平台认证(个人主体小程序不支持手机号授权功能,仅企业 / 个体工商户等认证主体可用);

  2. 已在 uniapp 项目中配置小程序 AppID:打开manifest.json → 「微信小程序配置」→ 填写正确的appid,并确保项目已关联对应的微信小程序;
  3. 小程序已开通「手机号快速填写」权限(默认开通,可在微信公众平台「功能」→「接口设置」中确认)。

整体流程概述

用户触发登录 → 获取微信登录临时凭证code → 后端用code换取session_keyopenid → 前端获取手机号授权凭证ivencryptedData → 前端将ivencryptedDatacode传给后端 → 后端解密获取手机号并完成登录 / 注册 → 返回用户登录态给前端。

步骤 1:前端实现(uniapp 代码)

关键点说明

  • 微信小程序的手机号授权必须通过button组件的open-type="getPhoneNumber"触发,无法通过 API 主动调用,这是微信的强制规范;
  • 需先调用uni.login()获取临时登录凭证code,用于后端换取微信开放平台的session_key(解密手机号必须用到session_key);
  • 授权结果通过@getphonenumber事件回调获取,包含加密的手机号数据和授权状态。
完整前端代码示例(Vue2/Vue3 通用,适配 uniapp)
<template>  <viewclass="login-container">    <!-- 手机号授权登录按钮(必须设置open-type="getPhoneNumber") -->    <button      open-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;  height100vh;}.login-btn {  width80%;  height88rpx;  line-height88rpx;  background-color#07c160;  color#fff;  border-radius44rpx;  font-size32rpx;}</style>
步骤 2:后端实现(核心:解密手机号)
核心依赖
  • 微信开放平台提供的解密算法,推荐使用官方封装的 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
2.后端接口代码示例:
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({ code400, msg"参数不全,请重试" });    }    // 2. 调用微信接口,换取session_key和openid    const 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"      },      jsontrue    });    // 3. 校验微信接口返回结果(是否获取到session_key)    if(wxRes.errcode) {      return res.json({ code500, msg"获取微信会话密钥失败" });    }    const { session_key, openid } = wxRes;    // 4. 解密手机号    const phoneData = decryptPhoneNumber(encryptedData, iv, session_key);    if(!phoneData.phoneNumber) {      return res.json({ code500, 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({      code200,      msg"登录成功",      data: {        token,        userInfo: {          id: userInfo[0].id,          phone: userInfo[0].phone,          nickname: userInfo[0].nickname || "微信用户"        }      }    });  } catch(error) {    console.error("手机号登录后端异常:", error);    res.json({ code500, msg"服务器内部错误" });  }});module.exports = router;
步骤 3:小程序端打包与调试
  • 本地调试:使用 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""// 用户输入的验证码      countDown0 // 验证码倒计时    };  },  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: { phonethis.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: {          phonethis.phoneNumber,          verifyCodethis.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 生成)必须放在后端。
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » uniapp 实现用户手机号授权登录完整流程

评论 抢沙发

2 + 8 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮