分享PHP扫码登录原理及实现技巧
发布时间:2022-07-29 12:36:29 所属栏目:PHP教程 来源:互联网
导读:由于扫码登录比账号密码登录更方便、快捷、灵活,在实际使用中更受到用户的欢迎。 本文主要介绍了扫码登录的原理及整体流程,包含了二维码的生成/获
|
由于扫码登录比账号密码登录更方便、快捷、灵活,在实际使用中更受到用户的欢迎。 本文主要介绍了扫码登录的原理及整体流程,包含了二维码的生成/获取、过期失效的处理、登录状态的监听。 扫码登录的原理 整体流程 为方便理解,我简单画了一个 UML 时序图,用以描述扫码登录的大致流程! 总结下核心流程: 请求业务服务器获取用以登录的二维码和 UUID。 通过 websocket 连接 socket 服务器,并定时(时间间隔依据服务器配置时间调整)发送心跳保持连接。 关于客户端标识 也就是 UUID,这是贯穿整个流程的纽带,一个闭环登录过程,每一步业务处理都是围绕该次的 UUD 进行处理的。UUID 的生成有根据 session_id 的也有根据客户端 ip 地址的。个人还是建议每个二维码都有单独的 UUID,适用场景更广一些! 关于前端和服务器通讯 前端肯定是要和服务器保持一直通讯的,用以获取登录结果和二维码状态。看了下网上的一些实现方案,基本各个方案都有用的:轮询、长轮询、长链接、websocket。也不能肯定的说哪个方案好哪个方案不好,只能说哪个方案更适用于当前应用场景。个人比较建议使用长轮询、websocket 这种比较节省服务器性能的方案。 开启 Socket 服务器 访问登录页面 可以看到用户请求的二维码资源,并获取到了 qid 。 获取二维码时候会建立相应缓存,并设置过期时间: 之后会连接 socket 服务器,定时发送心跳。 此时 socket 服务器会有相应连接日志输出: 用户使用 APP 扫码并授权 服务器验证并处理登录,创建 session,建立对应的缓存: Socket 服务器读取到缓存,开始推送信息,并关闭剔除连接: 前端获取信息,处理登录: 扫码登录的实现 注意:本 Demo 只是个人学习测试,所以并未做太多安全机制! Socket 代理服务器 使用 Nginx 作为代理 socke 服务器。可使用域名,方便做负载均衡。本次测试域名:loc.websocket.net websocker.conf server { listen 80; server_name loc.websocket.net; root /www/websocket; index index.php index.html index.htm; #charset koi8-r; access_log /dev/null; #access_log /var/log/nginx/nginx.localhost.access.log main; error_log /var/log/nginx/nginx.websocket.error.log warn; #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location / { proxy_pass http://php-cli:8095/; proxy_http_version 1.1; proxy_connect_timeout 4s; proxy_read_timeout 60s; proxy_send_timeout 12s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } Socket 服务器 使用 PHP 构建的 socket 服务器。实际项目中大家可以考虑使用第三方应用,稳定性更好一些! QRServer.php <?php require_once dirname(dirname(__FILE__)) . '/Config.php'; require_once dirname(dirname(__FILE__)) . '/lib/RedisUtile.php'; require_once dirname(dirname(__FILE__)) . '/lib/Common.php';/** * 扫码登陆服务端 * Class QRServer * @author BNDong */class QRServer { private $_sock; private $_redis; private $_clients = array(); /** * socketServer constructor. */ public function __construct() { // 设置 timeout set_time_limit(0); // 创建一个套接字(通讯节点) $this->_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socket" . PHP_EOL); * 启动服务 */ public function run() { $this->_clients = array(); $this->_clients[uniqid()] = $this->_sock; while (true){ $changes = $this->_clients; $write = NULL; $except = NULL; socket_select($changes, $write, $except, NULL); foreach ($changes as $key => $_sock) { if($this->_sock == $_sock){ // 判断是不是新接入的 socket if(($newClient = socket_accept($_sock)) === false){ die('failed to accept socket: '.socket_strerror($_sock)."n"); } $buffer = trim(socket_read($newClient, 1024)); // 读取请求 $response = $this->handShake($buffer); socket_write($newClient, $response, strlen($response)); // 发送响应 socket_getpeername($newClient, $ip); // 获取 ip 地址 } else { // 判断二维码是否过期 if ($this->_redis->exists(libCommon::getQidKey($key))) { $loginKey = libCommon::getQidLoginKey($key); if ($this->_redis->exists($loginKey)) { // 判断用户是否扫码 $this->send($key, $this->_redis->get($loginKey)); $this->close($key, $_sock); } $res = socket_recv($_sock, $buffer, 2048, 0); if (false === $res) { $this->close($key, $_sock); } else { $res && $this->log("{$key} clinet msg: " . $this->message($buffer)); } } else { $this->close($key, $this->_clients[$key]); } (编辑:开发网_开封站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


浙公网安备 33038102330459号