随着微信的普及,扫码登录方式越来越多的被现在的应用所采用。因为不需要记住密码,只要有微信账号就可以方便快捷的登录。微信开放平台原生支持扫码登录功能,但大部分人还是使用公众平台,所以扫码登录只能自己实现。这是基于微信公众平台带参数的临时二维码,结合Swoole的WebSocket服务实现扫码登录。大致流程如下:客户端打开登录界面,连接WebSocket服务,WebSocket服务生成一个带参数的二维码返回给客户端。用户扫描并显示带有参数的二维码。微信服务端回调扫码事件,通知服务端开发者。服务器通知WebSocket服务。WebSocket服务通知客户端登录成功连接到WebSocket服务。安装Swoole后,我们需要使用WebSocket服务。创建一个新的WebSocket服务非常简单:$server=newswoole_websocket_server("0.0.0.0",1099);$server->on('open',function(swoole_websocket_server$server,$request)use($config){echo"server:handshakesuccesswithfd{$request->fd}\n";});$server->on('message',function(swoole_websocket_server$server,$frame){});这里的消息回调不实用,因为所有的消息都是服务端发送的,但是必须设置一个。如果设置的端口号低于1024,必须要有root权限,服务器记得去防火墙打开这个端口。生成带参数的二维码客户端连接成功后,WebSocket服务需要生成带参数的微信二维码返回给客户端显示:$server->on('open',function(swoole_websocket_server$server,$request)使用($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秒,防止一个二维码被多次扫描使用。消息推送到客户端时,必须是json,方便客户端处理。客户端代码也很简单:constsocket=newWebSocket('ws://127.0.0.1:1099');socket.addEventListener('message',function(event){vardata=JSON.parse(event.data);if(data.message_type=='qrcode_url'){$('#qrcode').attr('src',数据.url);}});二维码显示在客户端后,回调扫描事件需要提示用户扫码。对于用户扫描临时二维码,微信会触发相应的回调事件,我们需要在回调事件中处理用户的扫码行为。其中,我们需要用到微信传递的一些参数:FromUserNamesenderaccount(一个OpenID)MsgType消息类型,eventEvent事件类型,subscribeEventKey事件KEY值,qrscene_为前缀,后面是二维码的参数值:EventKey微信扫描二维码后没有qrscene_前缀。收到微信回调后,我们首先要根据不同的事件类型做不同的处理:{//跟随return$this->subscribe($message);}if($message['Event']=='unsubscribe'){//取消订阅return$this->unsubscribe($message);}if($message['Event']=='SCAN'){//后面的扫描码return$this->scan($message);}}else{return"您好!欢迎使用SwooleWechat扫码登录";}这里只做一个说明关注事件的业务逻辑,其他按需要编码:publicfunctionsubscribe($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登录,玩得开心!";returnsprintf($msgTemp,$user['nickname'],$count);}这里的EventKey其实就是连接WebSocket的客户端文件描述符,获取扫描用户的OPEN_ID,根据用户的OPEN_ID获取用户信息,通知WebSocket服务,回复短信到微信这里比较麻烦的一点是如何通知WebSocket服务。我们知道处理微信回调的代码不在WebSocket服务上,那么不同服务器之间如何通信呢?Swoole官方提供了两种方案:监听一个额外的UDP端口和使用swoole_client作为客户端访问服务器。这里我们选择第二种方案。Swoole1.8版本支持一台服务器监听多个端口。我们向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端口会默认继承主服务器的协议设置,新协议必须单独调用设置新协议然后我们就可以通知WebSocket服务了扫码回调过程中:publicfunctionnotify($message){$client=newswoole_client(SWOOLE_SOCK_TCP);if(!$client->connect('127.0.0.1',$this->config['notify_port'],-1)){return"connectfailed.Error:{$client->errCode}\n";}$ret=$client->send($message);}在WebSocket中通知登录成功服务收到登录成功的通知后,可以根据需要处理用户信息,然后将用户信息传递给客户端的浏览器显示结果。还记得我们刚刚监听的TCP端口吗?可以在接收事件中处理:$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['昵称']]));}$serv->close($fd);});最后一个登录界面:总结整个过程不难,主要的两个难点分别对应连接用户的扫描用户和不同服务器之间的通信。我们的方案是将连接的文件描述符作为一个临时的二维码场景值(这里也可以使用redis存储映射关系),监听新的TCP端口接收通知消息,可以访问http://wechat。sunnyshift.com/index.php试试看,记得用电脑打开。公众号后台发送‘swoole-wechat’获取完整源码仓库地址,完成。