基于 Swoole 的微信扫码登录功能实现代码


Posted in PHP onJanuary 15, 2018

随着微信的普及,扫码登录方式越来越被现在的应用所使用。它因为不用去记住密码,只要有微信号即可方便快捷登录。微信的开放平台原生就有支持扫码登录的功能,不过大部分人还是在用公众平台,所以扫码登录只能自行实现。这里基于微信公众平台的带参数临时二维码,并且结合 Swoole 的 WebSocket 服务实现扫码登录。大体流程如下:

  1. 客户端打开登录界面,连接到 WebSocket 服务
  2. WebScoket 服务生成带参数二维码返回给客户端
  3. 用户扫描展示的带参数二维码
  4. 微信服务器回调扫码事件并通知开发者服务器
  5. 开发者服务器通知 WebSocket 服务
  6. WebSocket 服务通知客户端登录成功

连接 WebSocket 服务

安装完 Swoole 之后,我们需用使用到 WebSocket 服务。新建一个 WebSocket 服务非常简单:

$server = new swoole_websocket_server("0.0.0.0", 1099);
$server->on('open', function (swoole_websocket_server $server, $request) use ($config){
  echo "server: handshake success with fd{$request->fd}\n";
});
$server->on('message', function (swoole_websocket_server $server, $frame) {
});

这里的 message 回调其实用不到,因为都是服务端下发消息的,但是必须设定一个。设定的端口号如果低于 1024 则必须要有 root 权限,服务器记得去防火墙开启该端口。

生成带参数二维码

WebSocket 服务在客户端连接成功后,需要生成一张微信的带参数二维码返回给客户端展示:

$server->on('open', function (swoole_websocket_server $server, $request) use ($config){
  $app = Factory::officialAccount($config['wechat']);
  $result = $app->qrcode->temporary($request->fd, 120);
  $url = $app->qrcode->url($result['ticket']);
  $server->push($request->fd, json_encode([
    'message_type'  => 'qrcode_url',
    'url'    => $url
  ]));
});

我们在 open 回调中,生成一张临时的二维码,二维码的场景值就是客户端连接的文件描述符,这样就可以保证每个客户端的唯一性.有效时间设置为 120 秒,防止一张二维码被多次扫码使用。消息 push 到客户端的时候必须要 json,方便客户端处理。客户端代码也很简单:

const socket = new WebSocket('ws://127.0.0.1:1099');
  socket.addEventListener('message', function (event) {
    var data = JSON.parse(event.data);
    if (data.message_type == 'qrcode_url'){
      $('#qrcode').attr('src', data.url);
    }
  });

回调扫码事件

在客户端展示二维码后,需要提示用户扫码。对于用户扫临时的二维码,微信会触发相应的回调事件,我们需要在该回调事件中处理用户的扫码行为。其中我们需要用到微信传递过来的一些参数:

FromUserName  发送方帐号(一个OpenID)
MsgType      消息类型,event
Event      事件类型,subscribe
EventKey    事件 KEY 值,qrscene_为前缀,后面为二维码的参数值

这里要注意一点:微信已关注扫码推送的 EventKey 是没有 qrscene_ 前缀的,只有未关注扫码然后关注才有。

收到微信回调后我们首先要根据不同的事件类型做不同处理:

if ($message['MsgType'] == 'event'){
  if ($message['Event'] == 'subscribe'){ //关注
    return $this->subscribe($message);
  }
  if ($message['Event'] == 'unsubscribe') { //取消关注
    return $this->unsubscribe($message);
  }
  if ($message['Event'] == 'SCAN'){  //已关注扫码
    return $this->scan($message);
  }
}else{
  return "您好!欢迎使用 SwooleWechat 扫描登录";
}

这里只讲解一个关注事件的业务逻辑,其他根据需要自行编码:

public function subscribe($message){
  $eventKey = intval(str_replace('qrscene_', '', $message['EventKey']));
  $openId = $message['FromUserName'];
  $user = $this->app->user->get($openId);
  $this->notify(json_encode([
    'type' => 'scan',
    'fd'  => $eventKey,
    'nickname' => $user['nickname']
  ]));
  $count = $this->count($openId);
  $msgTemp = "%s,登录成功!\n这是你第%s次登录,玩的开心!";
  return sprintf($msgTemp, $user['nickname'], $count);
}

这里的 EventKey 实际上就是连接 WebSocket 的客户端文件描述符,获取到扫码用户的 OPEN_ID ,根据用户的 OPEN_ID 获取用户信息,通知 WebSocket 服务,响应文本消息给微信。

这里一个比较麻烦的点就是如何通知 WebSocket 服务,我们知道处理微信回调的代码是是不在 WebSocket 服务上的,那么不同 Server 间如何通信呢?Swoole 官方给出的解决方案有两个:

  1. 额外监听一个UDP端口
  2. 使用 swoole_client 作为客户端访问 Server

这里我们选择第二个方案,Swoole 1.8 版本支持一个 Server 监听多个端口,我们在 WebSocket 服务新增监听一个 TCP 的端口:

$tcp_server = $server->addListener('0.0.0.0', 9999, SWOOLE_SOCK_TCP);
$tcp_server->set([]);
$tcp_server->on('receive', function ($serv, $fd, $threadId, $data) {
  
});

主服务器是 WebSocket 或 Http 协议,新监听的 TCP 端口默认会继承主 Server 的协议设置,必须单独调用 set 方法设置新的协议才会启用新协议

然后我们就可以在扫码回调的进程中去通知 WebSocket 服务:

public function notify($message){
  $client = new swoole_client(SWOOLE_SOCK_TCP);
  if (!$client->connect('127.0.0.1', $this->config['notify_port'], -1)) {
    return "connect failed. Error: {$client->errCode}\n";
  }
  $ret = $client->send($message);
}

通知登录成功

在 WebSocket 服务收到登录成功的通知后,就可以根据需要处理一下用户信息,然后把用户信息传递给客户端的浏览器展示结果,还记得我们刚刚新监听的 TCP 端口吗?就可以在 receive 事件中处理:

$tcp_server->on('receive', function ($serv, $fd, $threadId, $data) {
  $data = json_decode($data, true);
  if ($data['type'] == 'scan'){
    $serv->push($data['fd'], json_encode([
      'message_type'  => 'scan_success',
      'user' => $data['nickname']
    ]));
  }
  $serv->close($fd);
});

最后登录的界面:

基于 Swoole 的微信扫码登录功能实现代码 

总结

整个过程并不难,主要的两个难点就是对应连接用户的扫码用户、不同 Server 之间的通信,我们的解决办法就是把连接的文件描述符作为临时二维码场景值(这里也可以采用 Redis 来存储映射关系)、监听新的 TCP 端口来接受通知消息。可以访问 http://wechat.sunnyshift.com/index.php 试试看,记得要用电脑打开。

以上所述是小编给大家介绍的基于 Swoole 的微信扫码登录功能实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
迪菲-赫尔曼密钥交换(Diffie?Hellman)算法原理和PHP实现版
May 12 PHP
php实现对象克隆的方法
Jun 20 PHP
使用图灵api创建微信聊天机器人
Jul 23 PHP
基于PHP技术开发客服工单系统
Jan 06 PHP
php中foreach结合curl实现多线程的方法分析
Sep 22 PHP
thinkPHP自动验证、自动添加及表单错误问题分析
Oct 17 PHP
php中分页及SqlHelper类用法实例
Jan 12 PHP
PHP实现求解最长公共子串问题的方法
Nov 17 PHP
thinkPHP5.1框架路由::get、post请求简单用法示例
May 06 PHP
解决在Laravel 中处理OPTIONS请求的问题
Oct 11 PHP
基于thinkphp6.0的success、error实现方法
Nov 05 PHP
PHP实现随机发扑克牌
Apr 22 PHP
详解PHP序列化和反序列化原理
Jan 15 #PHP
PHP使用两个栈实现队列功能的方法
Jan 15 #PHP
php获取ajax的headers方法与内容实例
Dec 27 #PHP
PHP实现求连续子数组最大和问题2种解决方法
Dec 26 #PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
Dec 26 #PHP
thinkphp5 URL和路由的功能详解与实例
Dec 26 #PHP
php-msf源码详解
Dec 25 #PHP
You might like
php实现redis数据库指定库号迁移的方法
2015/01/14 PHP
解决PHP里大量数据循环时内存耗尽的方法
2015/10/10 PHP
使用Codeigniter重写insert的方法(推荐)
2017/03/23 PHP
ThinkPHP框架结合Ajax实现用户名校验功能示例
2019/07/03 PHP
JS获取图片实际宽高及根据图片大小进行自适应
2013/08/11 Javascript
javascript客户端遍历控件与获取父容器对象示例代码
2014/01/06 Javascript
js网页右下角提示框实例
2014/10/14 Javascript
javascript中String对象的slice()方法分析
2014/12/20 Javascript
即将发布的jQuery 3 有哪些新特性
2016/04/14 Javascript
Bootstrap Metronic完全响应式管理模板之菜单栏学习笔记
2016/07/08 Javascript
很棒的js选项卡切换效果
2016/07/15 Javascript
Angular2 (RC4) 路由与导航详解
2016/09/21 Javascript
JavaScript之创意时钟项目(实例讲解)
2017/10/23 Javascript
mint-ui的search组件在键盘显示搜索按钮的实现方法
2017/10/27 Javascript
JavaScript作用域、闭包、对象与原型链概念及用法实例总结
2018/08/20 Javascript
bootstrap table表格插件之服务器端分页实例代码
2018/09/12 Javascript
详解vue项目中使用token的身份验证的简单实践
2019/03/08 Javascript
vue表单验证你真的会了吗?vue表单验证(form)validate
2019/04/07 Javascript
Javascript摸拟自由落体与上抛运动原理与实现方法详解
2020/04/08 Javascript
python 输出一个两行字符的变量
2009/02/05 Python
详解python的webrtc库实现语音端点检测
2017/05/31 Python
Python上下文管理器和with块详解
2017/09/09 Python
python pyecharts 实现一个文件绘制多张图
2020/05/13 Python
详解HTML5中div和section以及article的区别
2015/07/14 HTML / CSS
德国体育用品网上商店:SC24.com
2016/08/01 全球购物
Solaris操作系统的线程机制
2015/07/28 面试题
高校自主招生自荐信
2013/12/09 职场文书
秋季运动会广播稿大全
2014/02/17 职场文书
《飞向蓝天的恐龙》教学反思
2014/04/09 职场文书
2015感人爱情寄语
2015/02/26 职场文书
党员转正介绍人意见
2015/06/03 职场文书
天堂的孩子观后感
2015/06/11 职场文书
护士爱岗敬业心得体会
2016/01/25 职场文书
重温经典:乔布斯在斯坦福大学的毕业演讲(双语)
2019/08/26 职场文书
Python实现的扫码工具居然这么好用!
2021/06/07 Python
Java面试题冲刺第十七天--基础篇3
2021/08/07 面试题