Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Swoole's WebSocket service to log in by scanning code?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/03 Report--

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

客户端打开登录界面,连接到 WebSocket 服务

WebScoket 服务生成带参数二维码返回给客户端

用户扫描展示的带参数二维码

微信服务器回调扫码事件并通知开发者服务器

开发者服务器通知 WebSocket 服务

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(strreplace('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 官方给出的解决方案有两个:

额外监听一个 UDP 端口

使用 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);

});

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report