关于微信授权,在我没开始搞清楚概念之前我挨个试了3种授权(第三方平台代公众号发起网页授权、网站应用微信登录功能以及公众号网页授权),直到第三种才发现是符合当前需求的。
一. 需求
我们已经有了一个已认证的服务号,要开发一个问卷页面,展示用户头像 和昵称 。需要兼顾 移动设备和pc设备的微信内置浏览器 和外部浏览器。
用户打开以下分享链接或微信扫码可以填写问卷:
图1
场景1:用户通过微信内置浏览器打开(包括直接扫码进入页面)
如果用户通过微信内置浏览器打开,用户授权后,即可填写问卷:
图2
场景2:用户使用pc端微信外部浏览器打开
如果用户通过普通浏览器打开(谷歌、edge等)则展示二维码,让用户通过微信扫码授权:
图3
手机微信扫码授权流程:
图4
授权成功后,pc页面成功跳转:
图5
二、实现
通读微信公众号网页授权。
1. 如果只是要求微信内置浏览器打开
如果只是要求微信内置浏览器 打开,那么可以直接给用户下面的链接(用户同意授权获取code):
txt
https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=snsapi_userinfo&forcePopup=true#wechat_redirect
微信打开这个链接后就是上文中图2
的效果。
但这个链接在普通浏览器打开会是:
但我们要求普通浏览器也需要能够展示,所以请继续往下看。
2. 分享链接是中转界面
因为不知道用户会不会在微信内置浏览器内打开,所以要做一个中转界面,判断当前浏览器环境,而分享链接跳转的就是这个中转界面。
判断是否在微信浏览器内,如果是则跳转上面的链接,如果不是则展示图3
,让用户扫码授权。
js
const inWeChatBrowser = /micromessenger/i.test(navigator?.userAgent?.toLowerCase())
if (inWeChatBrowser) {
// 微信内置浏览器
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=snsapi_userinfo&forcePopup=true#wechat_redirect`
} else {
// 微信外部浏览器
// ...
}
}
3. 普通浏览器通过手机微信扫码授权进入页面
让用户扫码授权的这个码是后端提供的:
二维码解码后是下面链接:
txt
https://open.weixin.qq.com/connect/oauth2/authorize?appid=appid&response_type=code&scope=snsapi_userinfo&scene_id=scene_id&forcePopup=True&redirect_uri=手机授权界面(图4)#wechat_redirect
它和微信内置浏览器直接打开的链接有两点不一样:
- 内置浏览器直接打开了所以
redirect_uri
就是问卷的uri
,而手机扫码授权的redirect_uri
是授权的uri
(上文中的图4
)。 - 手机扫码授权的链接因为要和电脑的码联动,所以后端生成了唯一的
scene_id
,用于后续pc页面轮询验证扫码结果。
手机授权界面
手机微信扫描后端提供的授权码后,会重定向到手机授权界面,并在链接后面附带code
。(此时可以从链接上获取到code
和scene_id
)
再将code和scene_id交给后端,后端获取到用户信息进行存储,此时pc端的轮询也会得到结果:
三、踩坑
1. 需要配置js安全接口域名
如果报"redirect_uri域名与后台配置的不一致,错误码10003":
那可能是你的js安全接口域名未配置 或配置错误 ,需登录微信公共平台,打开公众号设置进行配置:
注意点:
- 记得将校验文件下载到前端项目根目录
- 只设置域名就好,不用加协议(如
https://www.baidu.com
是不行的, 得是www.baidu.com
)
2. hash路由的重定向
如果你的路由模式是hash路由,微信重定向携带的参数会被放在#
的前面,如:
js
let redirectUri = 'https://www.baidu.com/#/home'
// https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=snsapi_userinfo#wechat_redirect
// 以上链接会被重定向到:
// https://www.baidu.com/?code=xxx#/home
这时候用useRoute().query.code
是获取不到的,贴上一个方法直接用:
js
export function getQueryParamFromURL(url, paramName) {
try {
const parsedUrl = new URL(url);
const paramValue = parsedUrl.searchParams.get(paramName) ?? '';
return paramValue;
} catch (error) {
console.error(`Error processing URL or retrieving the parameter '${paramName}':`, error);
return '';
}
}
3. 获取到code后面的步骤交给后端
secret和access_token安全级别非常高,后面的操作应放在服务端完成。
4. 同意授权之前的重定向也会携带code
js
// https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=snsapi_userinfo&forcePopup=true#wechat_redirect
对于以上链接,文档上说用户同意授权才会发生重定向:
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
但实际上同意授权之前也会发生重定向到redirect_uri
并附带code
参数,只不过用这个code去获取userInfo获取的是"匿名用户"。
例如下面左图中的uri是:
https://xxx/?code=011cXi000jdnsS1eV1100FdqU60cXi0C&state=#/surveyPaper/9cb71a2d
这个code获取的用户信息是匿名用户,获取不到昵称、头像等信息;
使用完整服务并同意授权后右图的uri是:
https://xxx/?code=0913cp100ZsttS1jsu300E7DHJ33cp1j&state=#/surveyPaper/9cb71a2d
这个code是可以获取到用户信息的。
5. code been used
微信重定向携带的code,只能使用一次,多次使用会报{"errcode":40163,"errmsg":"code been used, hints: [ req_id: VHfwwa05052279 ]"}
解决方法是后端用redis存第一个code处理的结果,第二个过来去缓存里读,不再向微信发起请求。 这也是大部分的解决思路。参考文章: