0 元给网站接入微信扫码登录,用小程序就够了~(已测试可以实现)

AI
AI文摘 此内容由AI根据文章内容自动生成
AI Summary

✨ 前言

最近就在给我的网站增加微信扫码登录能力。但经过一番调研发现,微信官方的扫码登录方案要么要营业执照,要么每年交 300 块认证费。我这个简单的小项目犯不着搞这些复杂的东西。

最终经过一番查找,我发现用微信小程序就能完美绕过这些限制。

效果就是网站弹出一个小程序码:

图片[1]-0 元给网站接入微信扫码登录,用小程序就够了~(已测试可以实现)-大海资源库

手机微信扫一扫,网页端自动登录:

图片[2]-0 元给网站接入微信扫码登录,用小程序就够了~(已测试可以实现)-大海资源库

点赞关注,接下来我们就一起看看如何实现吧~

其他方案的限制

在开始之前,先来看下主流方案的对比表格:

方案前置条件费用体验
微信开放平台(网站应用)企业营业执照 + 开发者资质认证300 元(一次性)最标准,扫码直接授权
公众号扫码登录认证服务号300 元/年扫码关注后登录
小程序个人实名即可注册0 元扫码进小程序,确认后登录
  • 其实微信开放平台是最正统的方案,用户扫码后直接在微信里弹授权确认,体验也最丝滑。
    • 但它要企业资质,个人开发者压根注册不了
  • 公众号方案则需要认证服务号,一年 300,而且还得先有个公司主体
  • 而小程序方案只需要个人身份证实名注册,不需要花一分钱就能搞定

小程序不认证的代价是不能被搜索、不能开通支付、也不能被分享。

但咱们就是拿它做个登录跳板,用户只通过扫码进入,完全不需要这些功能。

原理:一码扫完,网页怎么就登录了?

整体流程大概是这样:

ChatGPT Image 2026年7月20日 19_58_38

简单看下核心步骤:

  1. ticket 是中转凭证,每次打开登录弹窗生成一个随机 ticket(32 位 hex),存到 Redis,设 5 分钟过期
  2. 小程序码通过 getwxacodeunlimit 接口生成,scene 字段里塞的就是 ticket,用户扫码后小程序从 scene 里取出来
  3. 小程序端调 wx.login() 拿到 code,连同 ticket 一起发给后端,后端用 code 调微信的 code2session 接口换到 openid,完成用户身份绑定
  4. 网页每隔 1~2 秒请求后端“这个 ticket 登录了没”,一旦后端标记成功,前端拿到登录态,弹窗关闭

核心就是,通过小程序从微信获取唯一标识,绑定到我们的网站里,实现一个唯一标识(微信号)对应一个用户。

有个细节得说明一下,小程序从微信获取到的唯一标识叫 openid,openid 只在一个应用里唯一。

什么意思呢?

就是说,假如一个用户在我的小程序里的 openid 是 123,那么我的小程序里不会有其他用户的 openid 是 123 了,但是不影响别人的小程序里有用户的 openid 是 123。

动手实现

1)注册个人小程序

去微信公众平台注册一个小程序账号,只需要实名 + 微信扫码验证就可以了:

注册完拿到 AppID 和 AppSecret,后面要用。

后端实现

这里用 Node.js 示例,其他语言同理。

生成 ticket + 小程序码:

// POST /api/auth/qr/createasync function createQrLogin() {  const ticket = crypto.randomBytes(16).toString('hex') // 32位 hex    // 存 Redis,5 分钟过期  await redis.set(`login:${ticket}`, JSON.stringify({ status: 'pending' }), 'EX', 300)    // 调微信接口生成小程序码  const accessToken = await getAccessToken(APP_ID, APP_SECRET)  const res = await fetch(    `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}`,    {      method: 'POST',      headers: { 'Content-Type': 'application/json' },      body: JSON.stringify({        scene: ticket,        page: 'pages/login/login', // 小程序里的登录确认页        check_path: false,        width: 280,      }),    }  )    const imageBuffer = await res.arrayBuffer()  return { ticket, qrcode: Buffer.from(imageBuffer).toString('base64') }}

这里有三个关键点:

  1. scene 最长只能 32 个可见字符,所以用 16 字节 hex 刚好
  2. check_path: false 让生成码时不校验页面是否已上线,方便开发阶段调试
  3. 返回的是图片二进制,转 base64 给前端展示

轮询状态接口:

// GET /api/auth/qr/status?ticket=xxxasync function checkStatus(ticket: string) {  const data = await redis.get(`login:${ticket}`)  if (!data) return { status: 'expired' }    const { status, token } = JSON.parse(data)  if (status === 'confirmed') {    await redis.del(`login:${ticket}`) // 一次性,用完删掉    return { status: 'confirmed', token }  }  return { status: 'pending' }}

确认登录接口(给小程序调的):

// POST /api/auth/mini/confirmasync function confirmLogin(ticket: string, code: string) {  // 1. 校验 ticket 存在且未过期  const data = await redis.get(`login:${ticket}`)  if (!data) throw new Error('二维码已过期')    // 2. 用 code 换 openid  const session = await code2Session(APP_ID, APP_SECRET, code)  const { openid } = session    // 3. 查找或创建用户,生成登录 token  const user = await findOrCreateUser(openid)  const token = generateToken(user)    // 4. 更新 ticket 状态  await redis.set(`login:${ticket}`, JSON.stringify({ status: 'confirmed', token }), 'EX', 60)    return { success: true }}

code2Session 就是调微信的 https://api.weixin.qq.com/sns/jscode2session,传 appid、secret、js_code,换回 openid。

小程序侧

小程序只需要一个"登录确认页",用户扫码后会自动跳到这个页面。

// pages/login/login.jsPage({  onLoad(options) {    // 从小程序码的 scene 里取 ticket    const ticket = decodeURIComponent(options.scene || '')    if (!ticket) {      wx.showToast({ title: '二维码无效', icon: 'error' })      return    }    this.confirmLogin(ticket)  },  async confirmLogin(ticket) {    try {      // 1. wx.login 拿 code      const { code } = await wx.login()            // 2. 请求你的后端 confirm 接口      const res = await wx.request({        url: `https://your-domain.com/api/auth/mini/confirm`,        method: 'POST',        data: { ticket, code },      })            if (res.data.success) {        wx.showToast({ title: '登录成功', icon: 'success' })        setTimeout(() => wx.navigateBack(), 1500)      }    } catch (err) {      wx.showToast({ title: '登录失败', icon: 'error' })    }  },})

页面 UI 可以做得简单一点:显示个 loading 动画 + "正在确认登录..."的文案就行,用户感知上就是扫完码等一秒就好了。

前端:展示二维码 + 轮询

// 伪代码,React 示例function LoginModal() {  const [qrcode, setQrcode] = useState('')  const [ticket, setTicket] = useState('')  useEffect(() => {    // 请求后端生成小程序码    fetch('/api/auth/qr/create', { method: 'POST' })      .then(res => res.json())      .then(({ ticket, qrcode }) => {        setTicket(ticket)        setQrcode(qrcode)      })  }, [])  useEffect(() => {    if (!ticket) return    // 每 1.5 秒轮询    const timer = setInterval(async () => {      const res = await fetch(`/api/auth/qr/status?ticket=${ticket}`)      const data = await res.json()      if (data.status === 'confirmed') {        clearInterval(timer)        // 拿到 token,登录成功!        login(data.token)      } else if (data.status === 'expired') {        clearInterval(timer)        // 提示过期,让用户刷新      }    }, 1500)    return () => clearInterval(timer)  }, [ticket])  return (    <div>      <img src={`data:image/png;base64,${qrcode}`} />      <p>请用微信扫一扫</p>    </div>  )}

注意事项 & 踩坑

scene 只有 32 字符。不要想着往里塞 JWT 或者 URL,只放一个 ticket ID 就行,其他信息让后端通过 ticket 去 Redis 里查。

小程序必须已发布getwxacodeunlimit 生成的码指向的是正式版小程序。如果你的登录页还没发布上线,用户扫了会报"页面不存在"。开发阶段可以设 check_path: false 先把码生成出来,但真机测试还是得至少上个体验版。

本地调试怎么搞? 不需要真机扫码。在微信开发者工具里添加一个编译模式,启动页面填 pages/login/login,启动参数填 scene=你的ticket,就能模拟扫码进入的效果。

真机测试走局域网。手机访问不了你电脑的 localhost,小程序里的 API 地址要改成你电脑的局域网 IP(比如 192.168.1.8:3000),同时开发者工具里关掉"域名校验"。

access_token 要缓存。微信的 access_token 有效期 2 小时,别每次生成码都重新获取,用 Redis 缓存一下。

© 版权声明
THE END
点赞8分享
及时反馈~ 抢沙发

请登录后发表评论

    暂无评论内容