Laravel中实现微信公众号登录

一般公司都会使用微信扫码登录,通过 PC 端打开

1
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

然后用户扫描网页上的二维码,手机端确认,即可在网页上登录。

但是这样的登录方式越来越不适用于这个流量时代,都希望可以通过关注公众号登录,这样不仅可以给公众号引流,还可以通过公众号推文或者模板消息等其他诱导方式,再次吸引用户重新打开网页。

so …

找到了看准网就实现了关注服务号自动登录,我很好奇的去看了一下他们的网络请求。

1
curl 'https://www.kanzhun.com/scan?wxScanId=2Lhy8vtcRg~~&_=1561996681530'

删除了其他参数,只留下核心的两个参数,wxScanId 是后端随机返回一个字符串,_ 貌似是一个计数器,暂时不深究,这个接口也很奇怪,每次都要 pending 30s 左右,我在思考这是 「长连接」?google 了一番,原来这里是一个专有名词 「长轮询」。

长轮询必然会对应短轮询,正常的 http 请求,一个 request 到后端服务器,这时候做相应的一个数据处理然后就立即返回了,对于刚才扫码登录这个业务所以短轮询会一遍一遍去查询服务器,当前这个 code 是否已经扫码。但是长轮询会将 request 发到服务端,这时候服务端会 hold 这个请求,通过后端去查询,没查到继续去查,查到可立即返回到客户端,大大降低了客户端的响应时间。

大致的流程:

  1. 服务端生成一个随机数 A
  2. 通过微信公众平台生成带 A 参数的二维码
  3. 将 A 存入到 redis 中,key 为 A ,value 为 false
  4. 将 A 和 二维码 url 返回到前端
  5. 客户端通过长轮询不断查询
  6. 用户扫描二维码可获取到 随机数对应的 openid
  7. 将 openid 存入到redis 中,key 为 A ,value 为 openid
  8. 客户端查询到结果通过 openId 登录

show me the code 。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 $ttl = 30; // 30秒 每秒查询一次
while ($ttl--) {
$openId = Cache::get(WeixinService::LOGIN_KEY_PREFIX . $request->code);
if ($openId === false) {
// 还未扫码 继续查询
} elseif ($openId) {
// 已经扫码,拿到 openId
return $openId;
} else {
// 码已经过期,或者无效码
$this->response->errorBadRequest('二维码已经过期,或者无效');
}
sleep(1);
}

这个接口的目的就是实现了上面的逻辑,因为我将数据放到 redis 中,这样服务端可以每隔 1s 查询一次,如果用户已经扫码,则微信服务器会将事件推送到我们自己的服务器,这时候拿到 openid,配置到 redis 中,从而长轮询取到 openid ,后面的就是简单的登录流程,直接省略了。

参考文档

坚持原创技术分享,您的支持将鼓励我继续创作!