With the popularity of WeChat, the scan code login method is increasingly used by current applications. It does not need to remember the password, as long as there is a WeChat account, you can log in conveniently and quickly. WeChat’s open platform natively supports scanning code login, but most people still use the public platform, so scanning code login can only be implemented by themselves. This is based on the temporary QR code with parameters of the WeChat public platform, and combined with Swoole's WebSocket service to achieve scan code login. The general process is as follows:
-
The client opens the login interface and connects to the WebSocket service
-
The WebScoket service generates a QR code with parameters and returns it to the client
-
The user scans the QR code with parameters
-
The WeChat server calls back the scan code event and notifies the developer server
-
The developer server notifies the WebSocket service
-
The WebSocket service notifies the client that the login is successful
Connect to WebSocket service
After installing Swoole, we need to use the WebSocket service. Creating a new WebSocket service is very simple:
$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) {
});
The message callback here is not practical, because all messages are sent by the server, but one must be set. If the set port number is lower than 1024, you must have root privileges. The server must remember to go to the firewall to open the port.
Generate QR code with parameters
After the WebSocket service is successfully connected to the client, a WeChat QR code with parameters needs to be generated and returned to the client to display:
$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
]));
});
In the open callback, we generate a temporary QR code. The scene value of the QR code is the file descriptor of the client connection, so that the uniqueness of each client can be guaranteed. The effective time is set to 120 seconds to prevent a QR code from being scanned multiple times. When the message is pushed to the client, json is required to facilitate the client's processing. The client code is also very simple:
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);
}
});
Callback scan code event
After displaying the QR code on the client, the user needs to be prompted to scan the code. For a user to scan a temporary QR code, WeChat will trigger a corresponding callback event, and we need to handle the user's scanning behavior in the callback event. Among them, we need to use some parameters passed by WeChat:
FromUserName 发送方帐号(一个OpenID)
MsgType 消息类型,event
Event 事件类型,subscribe
EventKey 事件 KEY 值,qrscene_为前缀,后面为二维码的参数值
Point to note here: micro letter has been concerned about the scan code push
EventKey
is noqrscene_
prefix, not only concerned about the scan code and then only concern.
After receiving the WeChat callback, we first need to do different processing according to different event types:
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 扫描登录";
}
Here only explain the business logic of a concerned event, and the others should be coded as needed:
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);
}
Here EventKey
is actually connected WebSocket client file descriptor, scan code to obtain the user OPEN_ID
, according to a user OPEN_ID
to obtain the user information, WebSocket service notification, response text message to a micro channel.
One of the more troublesome points here is how to notify the WebSocket service. We know that the code for processing WeChat callbacks is not on the WebSocket service, so how do different servers communicate? There are two solutions officially given by Swoole:
-
An additional UDP port is monitored
-
Use swoole_client as a client to access Server
Here we choose the second solution. Swoole 1.8 version supports one Server to monitor multiple ports. We add a TCP port to the WebSocket service:
$tcp_server = $server->addListener('0.0.0.0', 9999, SWOOLE_SOCK_TCP);
$tcp_server->set([]);
$tcp_server->on('receive', function ($serv, $fd, $threadId, $data) {
});
The main server is the WebSocket or Http protocol, the newly monitored TCP port will inherit the protocol settings of the main server by default, and the set method must be called separately to set the new protocol to enable the new protocol
Then we can notify the WebSocket service in the process of scanning the code callback:
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);
}
Notify login success
After the WebSocket service receives the notification of successful login, it can process the user information as needed, and then pass the user information to the client's browser to display the result. Remember the TCP port we just monitored? It can be processed in the receive event:
$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);
});
The last login interface:
Made plans too much trouble, have to go to http://wechat.sunnyshift.com/index.php
the test address to see.
Pay attention, don't get lost
Alright, everyone, the above is the entire content of this article. The people who can see here are all talents . As I said before, there are a lot of technical points in PHP, because there are too many, it is really impossible to write, and you will not read too much after writing it, so I will organize it into PDF and documents here, if necessary Can
Click to enter the secret code: PHP+「Platform」
For more learning content, please visit the [Comparative Standard Factory] excellent PHP architect tutorial catalog, as long as you can read it to ensure that the salary will rise a step (continuous update)
The above content hopes to help everyone . Many PHPers always encounter some problems and bottlenecks when they are advanced. There is no sense of direction when writing too much business code. I don’t know where to start to improve. I have compiled some information about this, including But not limited to: distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, Mysql optimization, shell scripts, Docker, microservices, Nginx, etc. Many knowledge points, advanced advanced dry goods, can be shared with everyone for free, and those who need can join my PHP technology exchange group