从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(06)

105 阅读3分钟

第 6 章:用户认证系统

“在互联网上,没人知道你是一条狗,除非你必须要登录。”

用户认证(Authentication)是所有应用的第一道防线。本章我们将深入探讨如何结合微信小程序生态,构建一套安全、无感的身份认证系统。

6.1 微信登录流程详解

在传统 Web 开发中,我们通常使用“账号 + 密码”登录。但在小程序里,这种方式既麻烦又不安全。我们利用微信提供的 login 能力,实现“一键登录”。

标准流程图 (The Flow)

sequenceDiagram
    participant U as 用户 (User)
    participant C as 小程序端 (Client)
    participant S as 云函数 (Cloud Function)
    participant DB as 云数据库 (DB)

    U->>C: 点击“一键登录”
    C->>S: 调用 auth/login
    Note over C,S: 云开发原生支持!\n无需 wx.login 获取 code\n云函数自动注入 OPENID
    S->>S: 获取 cloud.getWXContext()
    S->>DB: 查询 users 表 (where openid=xxx)
    alt 用户不存在
        S->>DB: 创建新用户 (Role='user')
    else 用户已存在
        S->>DB: 更新最后登录时间
    end
    S->>S: 生成 JWT Token
    S-->>C: 返回 Token + UserInfo
    C->>C: 存入 Pinia & Storage

关键点

  1. OpenID 自动注入: 只要在小程序端调用云函数,腾讯云网关会自动在 cloud.getWXContext() 中注入当前用户的 OpenID。完全不需要前端折腾 uni.login 获取 code 再去换取 SessionKey,省去了一大堆麻烦。
  2. 免密: 用户不需要输入密码,体验极佳。

6.2 JWT Token 生成与验证

虽然云函数调用自带 OpenID,但为了统一管理登录态(特别是未来如果扩展 H5 端),我们实现了一套 JWT (JSON Web Token) 机制。

6.2.1 为什么需要 JWT?

  • 无状态: 服务器不需要存储 Session,Token 本身包含了身份信息。
  • 角色管理: 我们将 role (user/admin) 签入 Token,前端拿到 Token 解析后即可知道用户权限,无需频繁查库。

6.2.2 生成 Token (Sign)

server/auth/index.js 中:

const token = jwt.sign(
  {
    userId: user._id,
    openid: user.openid,
    role: user.role // 核心:把权限写进 Token
  },
  process.env.JWT_SECRET, // 密钥,千万不能泄露!
  { expiresIn: '10d' } // 有效期 10 天
)

6.2.3 验证 Token (Verify)

我们在所有业务云函数(如 activity, vote)的入口处都有一个拦截器:

// 通用验证逻辑
const verifyJWT = (token) => {
  try {
    const decoded = jwt.verify(token, JWT_SECRET)
    return { valid: true, userId: decoded.userId, role: decoded.role }
  } catch (err) {
    return { valid: false, error: 'Token 过期' }
  }
}

6.3 角色权限设计

我们在 users 表中设计了 role 字段:

  • user: 普通用户。只能浏览活动、报名、投票、查看自己的记录。
  • admin: 普通管理员。只能管理自己创建的活动(编辑、下架、审核报名)。
  • super_admin: 超级管理员。系统的神。可以管理所有活动,可以设置某个用户为管理员,查看全平台数据。

越权防御: 即使前端黑客修改了 UI 显示出“删除按钮”,点击调用云函数时:

// server/activity/index.js
// 场景:删除活动
if (userRole !== 'super_admin' && activity.creatorId !== currentUserId) {
  return { code: 403, message: '大胆!竟敢动别人的活动!' }
}

后端校验是安全的最后一道防线,永远不要相信前端传来的数据

6.4 前端鉴权拦截器

在前端 src/utils/request.ts 中,我们封装了统一的请求逻辑:

  1. 请求拦截: 自动在 header 或 data 中携带 token
  2. 响应拦截:
    • 如果返回 code === 401 (未登录/过期),自动跳转到登录页,并清除本地缓存。
    • 如果返回 code === 403 (无权限),弹出提示“权限不足”。
// 伪代码示例
const request = async (options) => {
  const userStore = useUserStore()
  const token = userStore.token

  const res = await uniCloud.callFunction({
    name: 'server-api',
    data: { ...options.data, token } // 自动注入 Token
  })

  if (res.result.code === 401) {
    userStore.logout()
    uni.navigateTo({ url: '/pages/login/index' })
    return Promise.reject('Unauthorized')
  }
  return res.result
}

本章小结: 我们利用微信生态实现了便捷的登录,又通过 JWT 实现了灵活的权限控制。这套“微信 OpenID + JWT”的组合拳,既保证了安全性,又兼顾了用户体验。下一章,我们将开始构建活动管理的核心功能——活动的 CRUD。

下一章(07-活动管理模块)