公众号集成接入指南
在 CloudBase 控制台「集成中心」创建微信公众号集成后,平台会自动为你生成一个 HTTP 云函数,封装好网页授权、access_token 托管、JS-SDK、 模板/订阅消息、客服消息、菜单、二维码、用户管理、素材等全部能力。本指南只介绍网页授权(OAuth 2.0)——也是公众号集成最常用、最高频的能力,用于在公众号场景下让网页拿到用户的 openid 与(可选的)昵称/头像等信息。
关于函数名的约定:集成中心生成的云函数名形如
<集成名>-<随机串>(例如offiaccount-demo-rwmx67sc)。为方便讲解,本文统一称其为offiaccount-common——你在自己的代码中替换为实际函数名即可。
其他能力(access_token 托管、JS-SDK、模板/订阅消息、客服消息、菜单、二维码、用户管理、素材等)由同一个 offiaccount-common 云函数提供,本指南不展开。
整体调用链路:
- 网页跳转 → 微信授权页 → 微信回跳带回
code - 网页携带
code→ CloudBase 云 API 网关(accessToken 鉴权)→offiaccount-commonHTTP 云函数 → 微信sns/oauth2/access_tokenAPI offiaccount-common返回{ openid, access_token, ... }给网页或业务后端
架构总览
┌ ──────────────────┐
│ 公众号网页 │
│ (微信浏览器) │
└────────┬─────────┘
│ 1. 跳转 open.weixin.qq.com/connect/oauth2/authorize
│ ?appid=...&redirect_uri=...&scope=snsapi_base&state=...
▼
┌──────────────────┐
│ 微信授权页 │ 用户授权 / snsapi_base 静默
└────────┬─────────┘
│ 2. 回跳到 redirect_uri 携带 ?code=xxx&state=xxx
▼
┌──────────────────┐
│ 公众号网页 │
└────────┬─────────┘
│ 3. POST { code } 到 /oauth/token,带 Bearer accessToken
▼
┌──────────────────────────┐
│ CloudBase 云 API 网关 │ accessToken 鉴权
└────────┬─────────────────┘
│ HTTP invoke
▼
┌────────────────────────────┐
│ offiaccount-common │
│ POST /oauth/token │
│ → 调微信 sns/oauth2/... │
│ ← 返回 openid + tokens │
└────────────────────────────┘
职责分工:
| 角色 | 职责 |
|---|---|
| 公众号网页 | 引导用户跳转微信授权页;回跳后把 code 发给后端 |
| 云 API 网关 | 校验 Bearer {accessToken},将请求转发到 HTTP 云函数 |
offiaccount-common 云函数 | 持有 AppID + AppSecret,用 code 调微信换 openid 与 OAuth tokens;按需调用 sns/userinfo 拉用户资料 |
| 微信公众平台 | 颁发 code、openid,下发用户资料 |
| 集成中心 | 统一托管 AppID/AppSecret,作为环境变量注入云函数 |
前置条件
| 项 | 要求 |
|---|---|
| 公众号 | 服务号或已开通 snsapi_base 权限的订阅号;已通过微信认证 |
| 公众号 AppID + AppSecret | 持有可用值,AppSecret 可在公众平台 → 基本配置中重置 |
| 网页授权域名 | 公众平台 → 设置与开发 → 公众号设置 → 功能设置 → 网页授权域名,已添加发起授权的页面域名并完成 MP_verify_xxx.txt 校验 |
| CloudBase 环境 | 已开通;至少有一种认证登录方式可用(匿名登录不能调云函数,参见 4.3 节) |
第一步 · 准备公众号凭证
登录 微信公众平台 → 设置与开发 → 基本配置:
- AppID:形 如
wxc1546068399a59fc - AppSecret:点击「重置」生成,仅显示一次,妥善保存
AppSecret 不要泄露给前端,CloudBase 集成中心会把它托管并注入到云函数环境变量。
第二步 · 在 CloudBase 创建集成
2.1 进入集成中心
路径:云开发平台 → 模板与集成 → 集成中心 → 微信公众号 → 创建集成。
2.2 填写集成信息
控制台向导需填写:
- 集成名称(自定义,例如
offiaccount-demo) - 公众号 AppID 与 AppSecret
集成中心将统一托管上述凭证。
2.3 自动生成云函数资源
创建成功后,系统自动完成两 项操作:
- 按集成名称创建一个 HTTP 云函数(函数名形如
<集成名>-<随机串>,本文统称offiaccount-common)。 - 将 AppID、AppSecret 作为环境变量注入到该云函数。
第三步 · 了解 offiaccount-common 云函数
第二步创建集成时,平台已经把 AppID、AppSecret 作为环境变量注入到云函数。运行时通过 process.env 读取,业务代码中不会出现明文凭证。
如需修改配置,进入「集成中心 → 对应集成 → 编辑」,保存后平台将自动重新部署云函数。
3.1 网页授权相关路由
⚠️ 与「微信支付」集成的 pay-common 不同,
offiaccount-common不使用_action字段分发,而是直接使用 REST 路径。调用云函数时请使用完整路径,例如POST /oauth/token。
云函数对外暴露的网页授权 5 个 REST 路由:
| 路由 | 用途 | 必填请求体 |
|---|---|---|
POST /oauth/config | 返回当前集成配置的公众号 appId,便于前端拼授权 URL(避免在前端硬编码) | — |
POST /oauth/token | 用 code 换取 openid + OAuth access_token + refresh_token | { code } |
POST /oauth/refresh | 刷新 OAuth access_token(30 天有效的 refresh_token 换新的 2 小时 token) | { refresh_token } |
POST /oauth/userinfo | 用 OAuth access_token 拉取授权用户昵称/头像(仅 snsapi_userinfo scope 有效) | { access_token, openid } |
POST /oauth/verify | 校验 OAuth access_token 是否仍有效 | { access_token, openid } |
所有路由统一返回 { code: 0, msg: 'success', data: <业务数据> },错误返回 { code: -1, msg, data: null }。
这里的 OAuth
access_token与公众号的全局access_token(用于调菜单/消息接口)是两个不同的东西,前者只能用于拉取本用户的资料、不能调其他接口。
3.2 链路与鉴权
调用 offiaccount-common 的链路与 pay-common 完全一致:
前端登录 CloudBase 获取 accessToken → 携带 Authorization: Bearer {accessToken} 请求云 API 网关 → 网关分发至 offiaccount-common。
accessToken 必须来自「认证登录」——匿名登录拿到的 token 无权调用云函数,网关会直接返回 401。具体登录方式(邮箱、手机号、自定义登录等)请参考 CloudBase Web SDK · 认证登录。
第四步 · 接入网页授权
4.1 前置准备
配置网页授权域名
路径:微信公众平台 → 设置与开发 → 公众号设置 → 功能设置 → 网页授权域名。
下载 MP_verify_xxx.txt,放到 https://<你的域名>/MP_verify_xxx.txt 可访问的位置,再点击校验通过。
该域名必须是发起授权页的域名,不是回跳处理页的域名(虽然两者通常相同)。
理解两种 scope
| scope | 用户体验 | 能拿到什么 |
|---|---|---|
snsapi_base | 静默授权,无弹窗 | 仅 openid(多数业务用这个) |
snsapi_userinfo | 弹出授权确认页 | openid + 昵称 + 头像 + 性别等 |
4.2 完整网页授权流程
四步流程,前两步在前端完成,第三步走 offiaccount-common,第四步按需调用。
第 1 步:网页 → 跳转微信授权页
第 2 步:微信 → 回跳前端 redirect_uri,附带 ?code=xxx
第 3 步:前端 → POST /oauth/token { code } → 拿到 openid + access_token
第 4 步:前端 → POST /oauth/userinfo(仅 snsapi_userinfo 需要)
4.3 完整调用示例
适合微信内置浏览器中打开的公众号 H5 页面。
关键约束:
- 必须在微信内置浏览器中打开页面,外部浏览器(Chrome / Safari)打开微信授权页会跳到提示页
- 页面 URL 域名必须已配在公众号「网页授权域名」白名单
state参数必填,用于防 CSRF(前端生成随机串,回跳后校验)accessToken必须来自认证登录,详见 3.2
<!-- oauth.html 发起授权页 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>正在授权…</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/@cloudbase/js-sdk/dist/cloudbase.full.js"></script>
<script>
const ENV_ID = "your-env-id";
const FN_NAME = "offiaccount-common"; // 集成创建后生成的实际函数名
const app = cloudbase.init({ env: ENV_ID });
const auth = app.auth({ persistence: "local" });
// ⚠️ accessToken 必须来自「认证登录」,匿名登录无权调云函数
// 完整登录方式见 https://docs.cloudbase.net/api-reference/webv3/authentication#%E8%AE%A4%E8%AF%81%E7%99%BB%E5%BD%95
async function getAccessToken() {
// 示例占位:业务方需实现真实登录后返回 accessToken
throw new Error(
"请按 CloudBase Web SDK 文档实现认证登录后返回 accessToken"
);
}
async function callFn(token, path, body) {
const res = await fetch(
`https://${ENV_ID}.api.tcloudbasegateway.com/v1/functions/${FN_NAME}${path}?webfn=true`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(body || {}),
}
);
return res.json();
}
function unwrap(body) {
// offiaccount-common 直接返回 { code, msg, data },没有 wechatpay-node-v3 那层嵌套
return body && body.code === 0 ? body.data : null;
}
(async () => {
const token = await getAccessToken();
const url = new URL(location.href);
const code = url.searchParams.get("code");
const state = url.searchParams.get("state");
// —— 第 1 步:没有 code,发起授权 ——
if (!code) {
// 拿 AppID(也可直接在前端硬编码)
const { appId } = unwrap(await callFn(token, "/oauth/config")) || {};
if (!appId) {
alert("未取到 appId");
return;
}
const randomState = Math.random().toString(36).slice(2, 18);
sessionStorage.setItem("oauth_state", randomState);
// 当前页作为 redirect_uri,回跳后再次进入本脚本
const redirectUri = encodeURIComponent(location.href.split("?")[0]);
location.replace(
`https://open.weixin.qq.com/connect/oauth2/authorize` +
`?appid=${appId}` +
`&redirect_uri=${redirectUri}` +
`&response_type=code` +
`&scope=snsapi_base` + // 改成 snsapi_userinfo 可拉昵称/头像
`&state=${randomState}` +
`#wechat_redirect`
);
return;
}
// —— 第 2 步:回跳带回 code,校验 state ——
const expectedState = sessionStorage.getItem("oauth_state");
if (state !== expectedState) {
alert("state 校验失败,疑似 CSRF");
return;
}
sessionStorage.removeItem("oauth_state");
// —— 第 3 步:code 换 openid ——
const tokenRes = unwrap(await callFn(token, "/oauth/token", { code }));
if (!tokenRes || !tokenRes.openid) {
alert("换取 openid 失败");
return;
}
const { openid, access_token, refresh_token, scope } = tokenRes;
console.log("已拿到 openid:", openid, "scope:", scope);
// —— 第 4 步(可选):snsapi_userinfo 才能拉资料 ——
if (scope === "snsapi_userinfo") {
const info = unwrap(
await callFn(token, "/oauth/userinfo", {
access_token,
openid,
})
);
console.log("用户资料:", info); // { nickname, headimgurl, sex, ... }
}
// 业务侧:把 openid 写入会话 / 跳转到业务页
sessionStorage.setItem("openid", openid);
location.replace("/home");
})();
</script>
</body>
</html>
4.4 调用 /oauth/refresh 与 /oauth/verify
OAuth access_token 默认 2 小时过期。若要在长会话场景下复用 openid 关联的资料拉取能力,可:
// 续期:用 refresh_token 换新的 access_token(refresh_token 30 天有效)
const refreshed = unwrap(
await callFn(token, "/oauth/refresh", {
refresh_token,
})
);
// refreshed = { openid, access_token, refresh_token, expires_in, scope }
// 校验:判断 token 是否仍有效
const { valid } = unwrap(
await callFn(token, "/oauth/verify", {
access_token,
openid,
})
);
大多数业务不需要持久化保存 OAuth access_token——拿到 openid 即可入库关联用户,其他公众号能力(消息/客服等)走全局 access_token(由 offiaccount-common 的 /token/* 路由托管)。