文章目录
一、背景
在strapi v4中,我用了一款微信插件来实现微信的登录token授权。
具体可见:文末链接
目前该插件依然有人在用,但是作者似乎已经放弃维护,上一次更新还停留在853天,差不多3年了。
strapi5依然可用该插件,但我想自己动手开发个custom api
,用来实现类似的功能。
二、在strapi 5中开发微信登录模块
我们可以在 Strapi 中创建一个 自定义 API 接口,用于处理微信 code
并获取 openid
,然后存入数据库。
📌步骤大纲
- 创建一个新的 API 端点(
wx-auth
)用于处理code
换取openid
。 - 调用微信服务器 API,获取
openid
并返回给前端。 - 在 Strapi 数据库中存储
openid
(如果有必要)。
1. 创建 Strapi API 端点
你可以使用 Strapi 的 generate
命令创建 API:
# 在 Strapi 项目目录下运行
npx strapi generate api wx-auth
这将在 src/api/
目录下创建一个 wx-auth
目录,包含 controllers
、services
和 routes
文件。
如果使用以上指令报错,可以执行如下命令,引导式创建:
# 在 Strapi 项目目录下运行
strapi generate
选择api,输入api的名字,比如wx-auth,是否用于插件,选择N即可。
2. 编辑 wx-auth
控制器
修改 src/api/wx-auth/controllers/wx-auth.js
文件:
const axios = require('axios');
module.exports = {
async getOpenId(ctx) {
try {
const { code } = ctx.request.body;
if (!code) {
return ctx.badRequest('缺少 code 参数');
}
const appid = process.env.WX_APPID || '你的微信小程序AppID';
const secret = process.env.WX_SECRET || '你的微信小程序密钥';
// 请求微信 API 交换 openid
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
const response = await axios.get(url);
if (response.data.errcode) {
return ctx.badRequest('获取 openid 失败', response.data);
}
const { openid, session_key } = response.data;
return ctx.send({ openid, session_key });
} catch (error) {
ctx.internalServerError('服务器错误', error);
}
}
};
📌 说明:
- 这个 API 接收前端传来的
code
,然后调用 微信官方 API 获取openid
。 openid
作为唯一用户标识,可以存数据库,或者直接返回给前端。
🚀 进阶
如果你需要 存储访客信息,可以扩展 wx-auth
API,让它在 getOpenId
时 将 openid
存入 Strapi 数据库,例如:
const { createCoreController } = require("@strapi/strapi").factories;
const axios = require("axios");
module.exports = createCoreController("api::wx-auth.wx-auth", ({ strapi }) => ({
async getOpenId(ctx) {
try {
const { code } = ctx.request.body;
if (!code) {
return ctx.badRequest("缺少 code 参数");
}
const appid = process.env.WX_APPID;
const secret = process.env.WX_SECRET;
// 请求微信 API 获取 openid
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
const response = await axios.get(url);
if (response.data.errcode) {
return ctx.badRequest("获取 openid 失败", response.data);
}
const { openid, session_key } = response.data;
// 查询数据库是否已有该 openid
const existingUser = await strapi.db.query("api::wx-auth.wx-auth").findOne({
where: { openid },
});
if (!existingUser) {
// 如果用户不存在,创建新用户
await strapi.db.query("api::wx-auth.wx-auth").create({
data: { openid },
});
}
return ctx.send({ openid, session_key });
} catch (error) {
ctx.internalServerError("服务器错误", error);
}
},
}));
这样,每个 访客的 openid
都会 自动存入 Strapi 数据库,后续可以用于统计或管理。
如果不需要存储用户的openid,可以直接使用上面的简版-
wx-auth
控制器
注意:不要明文传输接口生成的session_key,否则会收到微信小程序的风控
如上,接口明文返回了session_Key
,不被允许,会收到微信小程序的风控提醒。
修复方法是,将src/api/wx-auth/controllers/wx-auth.js
中返回的session_Key
注释即可。
3. 添加 API 路由
修改 src/api/wx-auth/routes/wx-auth.js
,添加 POST /getOpenId
路由:
module.exports = {
routes: [
{
method: "POST",
path: "/getOpenId",
handler: "wx-auth.getOpenId",
config: {
auth: false, // 允许所有人访问
policies: [],
},
},
],
};
🔹 4. 在 .env
配置小程序 AppID
在 Strapi 项目的根目录 .env
文件中,添加微信小程序的 AppID 和密钥:
WX_APPID=你的微信小程序AppID
WX_SECRET=你的微信小程序密钥
小程序appID和秘钥,需要登录对应的小程序管理后台(https://mp.weixin.qq.com/)获取
然后 重启 Strapi 使配置生效:
npm run develop
🔹 5. 配置content-types
如果你重启 Strapi失败,getOpenId
接口报错,则可能还需要增加content-types
配置。
在wx-auth
文件夹下,新增content-types
文件夹,然后在下面再新增wx-auth
文件夹,最后新增一个schema.json
文件:
{
"kind": "collectionType",
"collectionName": "wx_auth",
"info": {
"singularName": "wx-auth",
"pluralName": "wx-auths",
"displayName": "WxAuth"
},
"options": {
"draftAndPublish": false
},
"attributes": {
"openid": {
"type": "string",
"unique": true
}
}
}
三、在小程序中使用 getOpenId 接口
在微信小程序 onLoad
事件中,调用 Strapi 端点获取 openid
:
onLoad() {
wx.login({
success: res => {
if (res.code) {
wx.request({
url: 'https://your-strapi-api.com/api/getOpenId',
method: 'POST',
data: { code: res.code },
success: res => {
if (res.data.openid) {
this.setData({ openid: res.data.openid });
} else {
wx.showToast({ title: '获取 openid 失败', icon: 'none' });
}
}
});
}
}
});
}
返回的数据如下:
{
"openid":"opU1T5YzVKSpA23LhVyIAq2cJiF4",
}
这样,你就可以拿到用户的唯一ID了。
四、与strapi-wechat-miniprogram-auth插件返回的数据对比
插件返回的数据如下:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjY3NTM3MzU5LCJleHAiOjE2NzAxMjkzNTl9.giRP146cEV0wyIh98D3KJigHShsEGofedtW5YYckzsQ",
"user": {
"id": 1,
"username": null,
"email": null,
"provider": "local",
"confirmationToken": null,
"confirmed": true,
"blocked": false,
"createdAt": "2022-11-04T02:41:27.149Z",
"updatedAt": "2022-11-04T02:41:27.149Z",
"openid": "oFHxc5TV5VKscIudqlmfx9JpK4d4",
"wechatUserInfo": {
"nickName": "wfz",
"gender": 0,
"language": "zh_CN",
"city": "",
"province": "",
"country": "",
"avatarUrl": "https://A-WECHAT-AVATAR-LINK"
}
}
}
可以看到,自己开发的api,返回的数据只有一个openid
,可能还不足以满足我们开发项目的实际需求,你也可以自行修改wx-auth.js
的代码,让其返回更多的数据,比如当前用户信息。
大概思路是,在前端调用wx.login
之前,先调用wx.getUserProfile
获取用户信息,比如展示一键登录按钮,获取成功后,再调用wx.login
,并且将wx.getUserProfile
获得的用户信息传递给wx-auth
进行转存和处理等。
你也可以查看插件原作者,是如何将用户信息、token等保存和返回给前端的:https://juejin.cn/post/7182122916505944124
简单的小程序代码:
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
const userInfo=res.userInfo;
wx.login({
success: res => {
// 发送res.code到后台换取openId,sessionKey,unionId
wx.request({
url: baseURL + '/api/getOpenId',
method: 'post',
data: {
code: res.code,
userInfo: userInfo
},
success(res3) {
//这里是后端处理后,返回的信息
}
})
}
})
}
})
注意:修改后,需要重启strapi服务。
✅ 总结
- 用户打开小程序,
wx.login()
获取code
。 - 前端发送
code
到 Strapi API (/api/getOpenId
)。 - Strapi 后端请求微信 API,换取
openid
并返回给前端。 - 前端拿到
openid
,在需要的地方使用。 - 如果简单项目可用自行开发的api,如果是复杂应用或需要获取
token
,可以考虑用插件
相关链接
- https://market.strapi.io/plugins/strapi-wechat-miniprogram-auth
- https://juejin.cn/post/7182122916505944124
如果你在开发strapi时遇到了问题,可以在下方与我联系,作为你的strapi免费顾问。