Events.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <?php
  2. /**
  3. * This file is part of workerman.
  4. *
  5. * Licensed under The MIT License
  6. * For full copyright and license information, please see the MIT-LICENSE.txt
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @author walkor<walkor@workerman.net>
  10. * @copyright walkor<walkor@workerman.net>
  11. * @link http://www.workerman.net/
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace app\worker;
  15. /**
  16. * 推送主逻辑
  17. * 主要是处理 onMessage onClose
  18. */
  19. use GatewayWorker\Lib\Gateway;
  20. use app\worker\Application;
  21. use think\facade\Config;
  22. use Lcobucci\JWT\Builder;
  23. use Lcobucci\JWT\Parser;
  24. use thans\jwt\provider\JWT\Lcobucci;
  25. use utils\Aes;
  26. class Events
  27. {
  28. // 使用TP框架
  29. public static function onWorkerStart()
  30. {
  31. $app = new Application;
  32. $app->initialize();
  33. }
  34. // 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定
  35. public static function onConnect($client_id)
  36. {
  37. Gateway::sendToClient($client_id, json_encode(array(
  38. 'type' => 'init',
  39. 'client_id' => $client_id
  40. )));
  41. self::onlineStatistics();
  42. }
  43. /**
  44. * 有消息时
  45. * @param int $client_id
  46. * @param mixed $message
  47. */
  48. public static function onMessage($client_id, $message)
  49. {
  50. // 客户端传递的是json数据
  51. $message_data = json_decode($message, true);
  52. if(!$message_data)
  53. {
  54. return ;
  55. }
  56. // 根据类型执行不同的业务
  57. switch($message_data['type'])
  58. {
  59. // 客户端回应服务端的心跳
  60. case 'pong':
  61. // 三次心跳后如果没有登录则断开连接
  62. $_SESSION['pong_times'] = ($_SESSION['pong_times'] ?? 0) + 1;
  63. $user_id=$_SESSION['user_id'] ?? '';
  64. $isQrLogin = $message_data['isQrLogin'] ?? 0;
  65. // 二维码登录逻辑,每60秒
  66. if($isQrLogin){
  67. // 生成新的二维码链接
  68. $domain=config('app.app_host');
  69. $appid=config('app.app_id');
  70. $token=urlencode(authcode($client_id,'ENCODE',$appid,100));
  71. $qrurl=rtrim($domain,'/') . '/scan/a/' . $token;
  72. Gateway::sendToClient($client_id, json_encode(array(
  73. 'type' => 'codeLoginQr',
  74. 'qrurl' => $qrurl,
  75. )));
  76. }
  77. // 如果是扫码登录则不做断开处理
  78. if(!$user_id && $_SESSION['pong_times'] >= 3 && !$isQrLogin){
  79. $_SESSION['pong_times'] = 0;
  80. self::closeClient($client_id);
  81. }
  82. break;
  83. case 'ping':
  84. self::sendStatus($client_id);
  85. break;
  86. case 'bindUid':
  87. self::auth($client_id,$message_data);
  88. break;
  89. }
  90. return;
  91. }
  92. protected static function sendStatus($client_id){
  93. $uid=$_SESSION['user_id'] ?? 0;
  94. $multiport=false;
  95. if($uid){
  96. $arr=Gateway::getClientIdByUid($uid);
  97. if(count($arr)>1){
  98. $multiport=true;
  99. }
  100. }
  101. Gateway::sendToClient($client_id, json_encode(array(
  102. 'type' => 'pong',
  103. 'multiport' => $multiport,
  104. )));
  105. }
  106. //验证用户的真实性并绑定
  107. protected static function auth($client_id, $msg){
  108. $token=$msg['token'] ?? '';
  109. $config = Config::get('jwt');
  110. $keys = $config['secret'] ?: [
  111. 'public' => $config['public_key'],
  112. 'private' => $config['private_key'],
  113. 'password' => $config['password'],
  114. ];
  115. $provider = new Lcobucci(new Builder(), new Parser(), $config['algo'], $keys);
  116. try {
  117. $token=str_replace('bearer ','',$token);
  118. $jwtData = $provider->decode((string)$token);
  119. } catch (\Exception $exception) {
  120. self::closeClient($client_id);
  121. }
  122. $userInfo = $jwtData['info']->getValue();
  123. //解密token中的用户信息
  124. $userInfo = Aes::decrypt($userInfo, config('app.aes_token_key'));
  125. //解析json
  126. $userInfo = (array)json_decode($userInfo, true);
  127. if(!$userInfo){
  128. self::closeClient($client_id);
  129. }
  130. $_SESSION['user_id']=$userInfo['user_id'];
  131. self::sendStatus($client_id);
  132. }
  133. //断开连接
  134. protected static function closeClient($client_id){
  135. self::onlineStatistics();
  136. $_SESSION['user_id']=null;
  137. Gateway::closeClient($client_id);
  138. }
  139. /**
  140. * 当断开连接时
  141. * @param int $client_id
  142. */
  143. public static function onClose($client_id)
  144. {
  145. $user_id=$_SESSION['user_id'] ?? '';
  146. if($user_id){
  147. Gateway::sendToAll(json_encode(array(
  148. 'type' => 'isOnline',
  149. 'time' => time(),
  150. 'data' => ['id'=>$user_id,'is_online'=>0]
  151. )));
  152. }
  153. self::onlineStatistics();
  154. }
  155. public static function onlineStatistics()
  156. {
  157. // 通知后台在线用户数和在线设备数
  158. $data=[
  159. 'type' => 'statistics',
  160. 'time' => time(),
  161. 'data' => [
  162. 'onlineCount'=>Gateway::getAllUidCount() ?? 0,
  163. 'clientCount'=>Gateway::getAllClientCount() ?? 0,
  164. ]
  165. ];
  166. Gateway::sendToGroup('admin-manage', json_encode($data));
  167. Gateway::sendToUid(1, json_encode($data));
  168. }
  169. }