产品经理一句"加个微信扫码登录呗",我吭哧吭哧折腾了半天。本以为就是调个官方 API 的事儿,结果不是二维码出不来,就是回调 404,再不就是 Code 换 Token 报错。
今天不整那些虚头巴脑的官方文档复读机,纯干货输出,把内嵌二维码 、后端换 Token 以及那些官方没明说的潜规则一次性盘明白。
1,先别急着写代码,得拿2个"通行证"
官方文档上来就扔 OAuth2.0 协议,看得人头大。咱们大白话翻译一下:微信登录就是让微信帮你验明正身。
在敲键盘之前,先把后勤保障做了(这一步没做,后面全是白干):
- 去微信开放平台注册(注意不是公众平台!这俩玩意儿不一样,开放平台是给网站/App用的)。
- 创建网站应用并等待审核 。审核通过后,你会拿到两个命根子:
AppID:相当于你的"用户名"。AppSecret:相当于你的"密码"。这东西打死不能放前端,泄露了你的应用就裸奔了!
- 配置"授权回调域名" :这地方是个大坑!填
www.yourdomain.com就行,不要带http://和https://,也不要带具体的路径。

2,两种登录方式,选哪个?
官方给了两种玩法,别纠结,按业务场景选:
| 方式 | 特点 | 适用场景 |
|---|---|---|
| 全页面跳转 | 跳到微信的页面扫码,完事跳回来。 | 传统老项目,不想折腾样式的。 |
| 内嵌二维码(重点) | 二维码直接躺在你网站页面上,体验丝滑。 | 现代 SPA 应用、追求用户体验的。 |
今天我们主要唠 内嵌二维码,毕竟这玩意看起来更高级(实际上坑也更多)。
3,实战:让二维码在你的页面上"原地出道"
首先,在 HTML 里放一个容器用来放置二维码:
html
<div id="wechat-login-box" style="width: 280px; height: 280px;"></div>
然后,引入微信的 JS 并实例化(vue可以在index.html加上):
js
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
初始化二维码控件:
html
<script>
const wxLogin = new WxLogin({
// false 就是用户扫完码,页面自动跳转
self_redirect: false,
// 容器 ID,必须精确
id: "wechat-login-box",
appid: "你的 AppID",
// 网页登录只认这个,千万别改!
scope: "snsapi_login",
// 注意!必须 URLEncode!
redirect_uri: encodeURIComponent("https//www.yourdomain.com/callback"),
// 防 CSRF 攻击的随机串,强烈建议加!
state: "123456",
// 听我的,填 1 用新 UI,好看多了
stylelite: 1,
// 默认 1(快速登录),想强制扫码就改成 0
fast_login: 1,
});
</script>
4,后端拿着 Code 去"换"用户信息
用户扫完码,微信会把浏览器重定向到你指定的 redirect_uri 上,并带上一个 code 参数,像这样: https://www.yourdomain.com/callback?code=071abc123&state=123456
这个 code 值存活 10 分钟,且只能用一次! 拿到它之后,赶紧让后端去微信的接口换 access_token。
这里是我的 Node.js (Express) 后端实战代码,注释里全是泪:
javascript
const axios = require('axios');
const crypto = require('crypto');
// 前端把 code 传过来
app.get('/api/wx/callback', async (req, res) => {
const { code, state } = req.query;
// 坑点预警:这里一定要校验 state!否则容易被 CSRF 攻击
if (state !== req.session.wxState) {
return res.send('State 校验失败,你小子是不是想黑我?');
}
try {
// 第一步:用 code + secret 换 access_token
const tokenUrl = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${APPSECRET}&code=${code}&grant_type=authorization_code`;
const tokenRes = await axios.get(tokenUrl);
const { access_token, openid, unionid } = tokenRes.data;
// 第二步:拿着 access_token 去拿用户头像、昵称
const userInfoUrl = `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}`;
const userRes = await axios.get(userInfoUrl);
// 第三步:存 session,返回前端
req.session.user = {
nickname: userRes.data.nickname,
avatar: userRes.data.headimgurl,
openid: openid,
unionid: unionid
};
// 重定向回首页
res.redirect('/');
} catch (error) {
// 坑点预警:如果是 40029,说明 code 过期或被使用了,别重试!
console.error('微信换 Token 失败了', error.response?.data || error.message);
res.send('登录超时,请重新扫码');
}
});
5,踩坑实录
我写代码时在这几个地方卡得怀疑人生,今天全给你抖出来:
坑 1:redirect_uri 的"域名必须完全匹配"玄学
- 表象:点击登录跳转后微信报错" redirect_uri 域名与后台配置不一致"。
- 真相 :开放平台配置的是
www.domain.com,你代码里写的是domain.com,少个www都不行!端口号也必须一致(80/443 除外)。
坑 2:内嵌二维码的 iframe 跳转问题
- 表象:用户扫完码,页面没反应,或者打开了新窗口。
- 真相 :如果你是在弹窗里或者特殊路由下使用,记得把
self_redirect设为true。设为true时,回调会在 iframe 内跳转,你需要监听 URL 变化自己拿 code,适合单页应用(SPA)的路由拦截。
坑 3:stylelite=1 时,自定义样式(href)直接失效
- 真相 :官方文档说得很隐晦。我一开始配了
href想改二维码大小,发现毫无卵用。查阅 FAQ 才发现:开启新 UI(stylelite=1),自定义 CSS 被官方强制覆盖了 。想自定义样式,只能退回stylelite=0用旧 UI。
坑 4:快速登录的"坑爹"用户体验
- 场景:产品经理发现,用户电脑登录着微信,点扫码居然直接一键登录了,连密码都不要,觉得不安全。
- 解决 :微信 3.9.11+ 默认开启快速登录。如果业务必须要求"本人手机扫码确认",传参时加上
fast_login: 0,强制弹出二维码。
坑 5:一个 Code 只能换一次 Token(巨坑!)
- 表象 :后端报
{"errcode":40163,"errmsg":"code been used"}。 - 原因:网络波动导致后端请求超时,前端傻傻地重试了一次,结果第二次就把第一次的 Code 给废了。
- 策略 :拿到 Code 后,后端必须做幂等处理或加锁,一旦换 Token 失败,直接让用户刷新页面重新拿 Code,千万别拿旧 Code 重试!
坑 6:用户拒绝授权咋办?
- 真相 :如果用户点了"拒绝",微信压根不会跳转回来!前端页面会一直卡在那。
- 对策 :监听
onQRcodeReady回调,设置一个定时器。如果 5 分钟内没有收到回调跳转,给用户一个"登录超时,请刷新重试"的按钮。
6,一张图看懂安全流程
为了防止前端同学被后端怼,把这张图甩给他:
text
前端页面 -> 展示二维码(用户扫码)
-> 微信回调后端(带上 code)
-> 后端用 AppSecret + code 请求微信 API(这一步只能后端做!)
-> 微信返回 access_token + openid
-> 后端查库 / 注册 / 登录,返回自定义 token
-> 前端拿到 token,登录成功!
切记 :绝对不要在前端里请求 https://api.weixin.qq.com/sns/oauth2/access_token,因为你的 AppSecret 根本藏不住,浏览器的 Network 面板一眼就能被人抄走!
7,最后说两句
微信登录这东西,文档写得像天书,但捋顺了其实就那么几步。核心就是:域名别配错、Code 别重放、Secret 别见光。
如果你按照这个教程走下来还是卡住了,别慌,90% 的锅都在 redirect_uri 的域名匹配上,建议直接去开放平台把那个域名复制出来,跟代码里的逐字比对。
希望兄弟们少掉几根头发,早点下班打游戏!有什么奇葩 Bug 欢迎评论区交流,咱们一起吐槽微信的文档。🤪
本次分享就到这儿啦,我是鹏多多,深耕前端的技术创作者,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
PS:在本页按F12,在console中输入document.getElementsByClassName('panel-btn')0.click();有惊喜哦~
往期文章
- 页面关闭照样接收推送:浏览器离线通知开发教程
- 新时代!OpenSpec+SDD规范驱动AI Agent开发项目实战指南
- Trae使用Pencil制作设计图以及迁移Figma设计稿的手把手教程
- 移动端H5项目,还需要react-fastclick解决300ms点击延迟吗?
- 纯前端提取图片颜色插件Color-Thief教学+实战完整指南
- react-konva实战指南:Canvas高性能+易维护的组件化图形开发实现教程
- React无限滚动插件react-infinite-scroll-component的配置+优化+避坑指南
- 前端音频兼容解决:音频神器howler.js从基础到进阶完整使用指南
- 使用React-OAuth进行Google/GitHub登录的教程和案例
- 纯前端人脸识别利器:face-api.js手把手深入解析教学
- 关于React父组件调用子组件方法forwardRef的详解和案例
- React跨组件数据共享useContext详解和案例
- Web图像编辑神器tui.image-editor从基础到进阶的实战指南
- 开发个人微信小程序类目选择/盈利方式/成本控制与服务器接入指南
- 前端图片裁剪Cropper.js核心功能与实战技巧详解
- 编辑器也有邪修?盘点VS Code邪门/有趣的扩展
- js使用IntersectionObserver实现目标元素可见度的交互
- Web前端页面开发阿拉伯语种适配指南
- 让网页拥有App体验?PWA 将网页变为桌面应用的保姆级教程PWA
- 使用nvm管理node.js版本以及更换npm淘宝镜像源