最近项目有个需求要增加一个登录方式。通过扫码关注公众号实现登录。而如今,随着微信普及程度大大增加,通过微信扫描网站的二维码进行登录,不仅减少了用户注册成本,而且能够实现用户的引流,极大提高用户体验。(做完才知道个人订阅号没有权限获取带场景的二维码)
实现思路
- 使用公众号接口生成二维码。
- 系统接收微信推送过来的事件(关注/扫码)。
- 用户点击关注或者扫描二维码后台都会接收到推送通知,然后根据通知实现自己的业务就可以了。
先来看看整个流程图的时序图
来捋一下整个流程:
- 先是用户来访问咋们的网站
- 咋们的网站肯定是要给他一个二维码扫,肯是需要去请求微信开放平台
- 用户扫码关注后,我们就会收到微信的回调事件,我们只需要处理这个回调事件就知道这个用户登录了。然后获取该用户的基本信息完成登录。
代码实现
以下代码是采用
fastapi框架实现,redis是采用aioredis
1.获取access_token.可以把获取的access_token存入缓存redis中,并设置过期时间,这样中就不用重复调用。
async def get_access_token():
"""
获取到的凭证
return {"access_token":"ACCESS_TOKEN","expires_in":7200}
"""
#1、获取access_token
url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}".format(settings.APPID,settings.APPSECRET)
#2、存入redis
if await redis_tools.is_existsKey("access_token"):
return await redis_tools.get("access_token")
else:
res = requests.get(url).json()
await redis_tools._set("access_token",res.get("access_token"))
return res.get("access_token")
redis_tools是自己封装的redis工具类。上述的代码实现获取access_token。先去判断redis中有没有存在这个access_token这键,没有就去微信公众平台获取。
2.获取生成二维码的ticket,这个也是同样的做法。代码如下:
async def generate_qrcode(token:str):
"""
生成二维码
return
{
"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==",
"expire_seconds":60,
"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"
}
"""
url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}".format(token)
data = {
"expire_seconds": 604800,
"action_name": "QR_SCENE",
"action_info": {
"scene": {
"scene_id":123
}
}
}
#判断redis是否存在ticket
if await redis_tools.is_existsKey("access_token"):
return await redis_tools.get("ticket")
else:
res = requests.post(url,data).json()
await redis_tools._set("ticket",res.get("ticket"),res.get("expire_seconds"))
return res.get("ticket")
其中scene_id是根据自己的业务来填具体的值。详细的用法请参考手册
这样前端页面就可以获取到这个ticket,然后携带这个值,去请求https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={}这个地址就可以获取到一个带参数的二维码了。
扫描带参数二维码事件
用户扫描带场景值二维码时,可能推送以下两种事件:
- 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
- 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
1、用户未关注时,进行关注后的事件推送
推送XML数据包示例:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,event |
| Event | 事件类型,subscribe |
| EventKey | 事件KEY值,qrscene_为前缀,后面为二维码的参数值 |
| Ticket | 二维码的ticket,可用来换取二维码图片 |
2、用户已关注时的事件推送
推送XML数据包示例:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,event |
| Event | 事件类型,SCAN |
| EventKey | 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id |
| Ticket | 二维码的ticket,可用来换取二维码图片 |
3、前端通过轮询方式检查用户是否关注或者扫描了
微信用户通过扫描上述生成带场景值的二维码,实现扫描关注登录。关键代码如下:
$.get("/wxmp/weChatQrCode", function (res) {
counter = 0
$("#qrcode").attr("src", res.data.qrcode_url);
flag = res.data.scene_value;
// 轮询登录状态
timer = setInterval(() => {
if (counter >= 30) {
clearInterval(timer);
return
}
// 请求参数是二维码中的场景值
$.get("/wxmp/?wechat_flag=" + flag, function (res) {
counter += 1
if (res.code == 200) {
sessionStorage.setItem("token", res.data.token)
window.location.href = '/chatgpt';
}
})
}, 2000);
})
效果展示
项目已经在gitee上开源了,可以关注微信公众号回复mp_chatgpt获取。