说实话,以前做一个微信小程序登录,最少要搞一个后端服务器,还得配数据库、写接口、处理 session。现在用 uni-app 自带的 uniCloud,前端加云函数就能搞定完整注册登录流程,不用买服务器,不用备案域名,更不用碰 Java 或者 PHP。这篇直接手把手带你写一个。
准备工作
你需要一个 HBuilderX 工具,目前最新版已经内置了 uniCloud 支持。然后创建一个默认模板的 uni-app 项目。
创建完之后,在项目根目录的 uniCloud 文件夹上右键,选择“创建云服务空间”,按提示注册并开通。这里会免费送一个阿里云或腾讯云的服务空间,每个月有免费额度,个人测试绝对够用。
之后在 uniCloud 目录下右键,选择“新建云函数”,起名 login。等下主要代码都写在这里面。
整体的登录流程
- 前端调用
uni.login拿到微信的临时 code。 - 前端把 code 发给云函数
login。 - 云函数拿着 code 去微信服务器换 openid(用户唯一标识)。
- 云函数用 openid 查数据库里的 users 表。
- 如果用户存在,直接返回登录成功;如果不存在,自动创建一条新用户记录。
- 最后把 openid 或自定义 token 返回给前端,前端存到本地。
数据库集合:users
先去 uniCloud 控制台,找到你的服务空间,创建一个集合(数据库表),名字叫 users。为了省事,字段先不用建,云函数里写啥就是啥。不过你最好手动加两个索引,不然以后查询会慢。
云函数代码:login/index.obj.js
右键云函数目录,打开 index.obj.js 文件,你可以把里面默认代码全部删掉,换成下面这段。注意我这里是用了 uniCloud 的云对象写法,你也可以用普通云函数,但云对象调用起来更顺手。
// 云对象:login
const uniID = require('uni-id') // 官方账号管理库,先不用管,后面会解释
module.exports = {
_before: function() {
// 可以做一些参数校验
},
async loginByWechat(code) {
// 1. 调用 uniCloud 内置的微信登录 API,获取 openid
// 注意:这里需要在前台配置 appid 和 secret,或者使用云函数URL化
const wxRes = await uniCloud.getPhoneNumber({
code: code,
provider: 'weixin'
})
// 但上面是获取手机号的,获取 openid 需要用 uniCloud.getOpenId()
// 为了清晰,我改用 uni-cloud 官方推荐的 uni-id 方法:
const res = await uniID.loginByWeixin({
code: code
})
if (res.code !== 0) {
return {
code: 100,
msg: res.msg || '微信登录失败',
}
}
// 2. 此时已经拿到 userInfo,包含 openid、unionid、nickname 等
const { openid } = res.userInfo
// 3. 查一下 users 集合有没有这个 openid
const db = uniCloud.database()
const users = db.collection('users')
const userRes = await users.where({ openid: openid }).get()
let userData = null
if (userRes.data.length === 0) {
// 不存在就创建,默认给个注册时间
const newUser = {
openid: openid,
nickname: '微信用户' + Math.floor(Math.random() * 10000),
avatar: '',
createTime: Date.now()
}
const addRes = await users.add(newUser)
userData = {
id: addRes.id,
...newUser
}
} else {
userData = userRes.data[0]
}
// 4. 生成一个简易 token(也可以自己写个随机串存起来)
const token = 'token_' + Date.now() + '_' + Math.random().toString(36).slice(2, 10)
// 5. 把这个 token 更新到用户记录
await users.doc(userData.id).update({ token: token, lastLoginTime: Date.now() })
return {
code: 0,
msg: '登录成功',
data: {
token: token,
userInfo: {
id: userData.id,
nickname: userData.nickname,
avatar: userData.avatar
}
}
}
}
}
云对象调用方式是在前端直接用 uniCloud.importObject('login'),然后调里面的方法。如果你没有装 uni-id,也可以不依赖它,直接自己用 uniCloud.httpclient 请求微信接口,但那样会麻烦一些。
没有 uni-id 怎么办?
上面代码用了 uniID.loginByWeixin,这需要你在 uniCloud 的插件市场安装 uni-id-pages 组件。更简单的办法:我们可以完全不用 uni-id,直接手动用 code 换 openid。微信小程序的接口是:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code
云函数里可以用云函数内置的 uniCloud.httpclient 发请求。这样就不需要额外装插件,更加“原教旨”。我把修改后的核心代码写一下:
// 云函数中,在 config.json 里配置你自己的 appid 和 secret(好习惯别写死)
// 然后用云函数中提供的 httpclient 去请求微信服务器
const crypto = require('crypto')
module.exports = {
async getOpenId(code) {
const appid = '你的AppID'
const secret = '你的AppSecret'
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`
const res = await uniCloud.httpclient.request(url, { dataType: 'json' })
if (res.data && res.data.openid) {
return res.data.openid
} else {
throw new Error('微信 code 无效')
}
}
}
拿到 openid 后,剩下的逻辑就和之前一样了。其实大多数人现在都直接用 uni-id,因为还能顺便管注册、密码、手机号等。不过如果你只需要一个最简登录,自己搞更清晰。
前端页面:login.vue
在 pages 目录新建一个 login.vue,页面里放一个按钮,点击后调起微信登录。
export default {
methods: {
async wechatLogin() {
// 调起微信登录,得到 code
const loginRes = await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: resolve,
fail: reject
})
})
if (!loginRes.code) {
uni.showToast({ title: '获取code失败', icon: 'none' })
return
}
// 导入云对象
const loginObj = uniCloud.importObject('login')
const res = await loginObj.loginByWechat(loginRes.code)
if (res.code === 0) {
// 存 token 和用户信息
uni.setStorageSync('token', res.data.token)
uni.setStorageSync('userInfo', res.data.userInfo)
uni.showToast({ title: '登录成功' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' }) // 跳转到首页
}, 500)
} else {
uni.showToast({ title: res.msg, icon: 'none' })
}
}
}
}
注意:如果你用的不是微信小程序,而是 App 端,uni.login 的 provider 要换成对应的参数,不过今天重点讲小程序。
真机测试要注意的几个坑
- appid 必须是小程序自己的,在 HBuilderX 里填到 manifest 的微信小程序配置中。
- 云函数要部署,右键云函数目录选择“上传部署”,云端运行。
- 账号鉴权,在云函数指向的数据库权限中,建议设置为“仅云函数可读写”或者自定义安全规则,别放所有人直接读。
- 时间戳和 token 的安全性,生产环境建议用 uni-id 的 token 体系,我自己练手就用随机串了。
- 别忘了在微信公众平台把 request 合法域名加上,但如果用云函数没有这个问题,因为是云函数发起的请求。
更进一步:注册和登录合并了?
很多应用要在登录前让用户填昵称头像,这时候你可以先登录(拿到 openid),然后进去后再引导用户完善资料。在 cloud 函数中,如果查到用户已经存在就直接返回,否则就自动创建一个基础账号。这样注册和登录实际上是一个接口,用户无感知。
如果你非要区分“注册”和“登录”,可以在云函数上加一个参数,比如 type: 'login' 或 'register'。但实际业务中很少有人会去区分,因为微信授权默认就是“登录即注册”。
前端如何判断是否已登录?
在 App 启动的时候,从 storage 里取 token,如果存在就当作已登录。但这不安全,建议每次进需要登录的页面时,再调用一个云函数校验 token 是否有效。这里简单起见,只做了前端本地存储。
你可以写一个公共方法:
async function checkLogin() {
const token = uni.getStorageSync('token')
if (!token) return false
const obj = uniCloud.importObject('user')
const res = await obj.checkToken(token)
return res.code === 0
}
云函数 user 里再写一个 checkToken 方法,拿 token 去数据库查询是否存在,顺便把用户信息返回。这就是完整的一套会话管理了。
最后说点题外话
用 uniCloud 做微信登录真的舒服,你不需要理解 HTTP 服务器怎样处理跨域,不用考虑密钥怎么存,因为云函数天然就是一个服务端环境。而且免费额度对于个人项目完全够用。
当年我们写登录还要自己用 session 或 JWT,现在一个云函数两分钟搞定,前端代码也少得可怜。唯一要注意的就是微信 appSecret 千万别暴露在代码里,一定要写到云函数环境变量或者单独配置文件里。
对了,记得在云函数配置里加上权限校验,不然任何人随便调用你云函数可能会白嫖你的数据库资源。
试试看吧,三分钟就能跑通。

