With the popularity of wechat, scanning code login is increasingly used by current applications. Because it does not need to remember the password, as long as there is a wechat signal can be convenient and fast login. Wechat’s open platform supports the function of scanning code login, but most people are still using the public platform, so the scanning code login can only be realized by themselves. This is based on the temporary TWO-DIMENSIONAL code with parameters of wechat public platform, and combined with The WebSocket service of Swoole to realize the scanning code login. The general process is as follows:
- The client opens the login page 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 displayed
- The wechat server calls back the code sweep event and notifies the developer server
- The developer server notifies the WebSocket service
- The WebSocket service notifies the client of successful login
Connect to the WebSocket service
Once Swoole is installed, we need to use the WebSocket service. Creating a 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) {});Copy the code
The message callback here is not really needed because the server sends messages, but one must be set. If the port number is lower than 1024, you must have the root permission. The server must enable the port on the firewall.
Generate a QR code with parameters
After the WebSocket service is successfully connected to the client, it needs to generate a wechat QR code with parameters and return it to the client for 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
]));
});
Copy the code
In the open callback, we generate a temporary QR code whose scene value is the file descriptor for the client connection to ensure that each client is unique. The valid time is set to 120 seconds to prevent one QR code from being scanned for multiple times. The message pushed to the client must be JSON for the client to process. The client code is also 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); }});Copy the code
Callback sweep event
After displaying the QR code on the client, you need to prompt the user to scan the code. For the user to scan the temporary QR code, wechat will trigger the corresponding callback event, and we need to process the user’s code scanning behavior in this callback event. Among them, we need to use some parameters transmitted by wechat:
FromUserName Sender account (one OpenID) MsgType Message type, event Event event type, SUBSCRIBE EventKey Event KEY value, qrScene_ prefix, followed by the parameters of the QR codeCopy the code
Note here: Wechat scan push EventKey does not have the prefix qrscene_, only not scan and then follow.
After receiving wechat callback, we should first do different processing according to different event types:
if ($message['MsgType'] = ='event') {if ($message['Event'] = ='subscribe') {/ / concernreturn $this->subscribe($message);
}
if ($message['Event'] = ='unsubscribe') {// Unfollowreturn $this->unsubscribe($message);
}
if ($message['Event'] = ='SCAN'){// Focus on scanningreturn $this->scan($message); }}else{
return "Hello! Welcome to swolewechat scan login.";
}
Copy the code
Only one event-focused business logic is explained here, and the rest is 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, login successful! \n This is your %s login, have fun!";
return sprintf($msgTemp.$user['nickname'].$count);
}
Copy the code
The EventKey here is actually the client file descriptor that connects to WebSocket, obtains the OPEN_ID of the scanned user, obtains the user information according to the OPEN_ID of the user, informs the WebSocket service, and responds to the text message to wechat. A troublesome point here is how to notify the WebSocket service, we know that the code handling wechat callback is not on the WebSocket service, so how to communicate between different servers? Swoole’s official solution is two:
- Listen for an additional UDP port
- Use swoole_client as the client to access the Server
Here we choose the second solution, Swoole 1.8 supports a Server to listen on multiple ports, we add a TCP port in 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) {});Copy the code
The primary Server is WebSocket or Http. The TCP port monitored by the new Server inherits the protocol Settings of the primary Server by default. The new protocol can be enabled only after the set method is invoked to set the new protocol
We can then notify the WebSocket service in the sweep callback process:
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);
}
Copy the code
Notification of Successful Login
After the WebSocket service receives a successful login notification, it can process the user information as needed and then pass the user information to the client’s browser to display the results. Remember the TCP port we just listened on? You can handle it 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);
});
Copy the code
Last login interface:
conclusion
The whole process is not difficult. The two main difficulties are the scanning users corresponding to the connected users and the communication between different servers. Our solution is to take the connected file descriptor as the temporary TWO-DIMENSIONAL code scene value (Redis can also be used to store the mapping relationship here) and listen to the new TCP port to receive notification messages. Can visit http://wechat.sunnyshift.com/index.php try, remember to use a computer to open.
Public number background hair ‘swoole-wechat’ to obtain the complete source code warehouse address, over.