php/socket.io实现扫码登录

首先来先看张流程图,了解下原理


php/socket.io实现扫码登录_第1张图片服务环境

后端web服务器: 80端口 php 负责生成二维码 生成uuid(这个很重要,后边会提到)  解析token等

web端:  javascript 实现链接建立,信息发送

app端:  javascript 实现登录确认(惭愧 ios学了好久就会加载个webview,网络编程更是一塌糊涂,只能拿webview代替原生了)

socket服务端:8888端口 node.js  负责管理socket链接 交换数据


首先是socket服务器

          为什么决定用node来实现呢?其实刚开始准备用php的socket来实现,结果发现php socket网上几乎找不到什么资料,好不容易找到几个,一看日期,好家伙,2006年的,果断不敢用,随后发现了一个php的socket框架workerman,框架封装的很好,几乎不需要考虑套接字,协议也可以自己定义,但是我只想做一个demo,正巧最近被逼着搞node,于是就决定用node来做后端.


搭建socket服务器

          node服务器的搭建非常简单,在我另一篇博文中有详细介绍,这里不多介绍,详见,http://blog.csdn.net/zhangsheng_1992/article/details/51322707 整个socket服务需要三个扩展: 

          socket.io  一看就是提供服务的

          winston    这个是记录日志的,方便调试

          express    这个是干啥的我也不知道,不过不装的话老报错,node二把刀水平,有知道的大神还望告知,

          安装包很简单

          首先建立个package.json文件,这个文件你可以理解成apache的httpd.conf

          内容如下:

         

{
  "name": "IoService",
  "version": "0.0.1",
  "description": "Socket.io Service",
  "dependencies": {
    "express": "^4.13.4",
    "socket.io": "^1.4.6",
    "winston": "^2.2.0"
  }
}

        其中dependencies由脚本生成,无需填写,然后安装扩展,记着root权限哦

       

npm install --save express
npm install --save socket.io
npm install --save winston

       然后编写个conf.json的脚本配置文件,本身没多少东西,你也可以直接写进代码里面,内容如下

{
    "port": 8888,
    "log_level_logger": "info"
}

     最后编写服务端代码: 如下

//加载配置文件
var fs = require('fs');
var file = __dirname + "/" + 'conf.json';
var config = JSON.parse(fs.readFileSync(file, 'utf8'));
var port = config['port'];
var loggerLevel = config['log_level_logger'];

//初始化日志记录
var winston = require('winston');
var logger = new (winston.Logger)({
    transports: [
        new (winston.transports.Console)({level: loggerLevel, timestamp: true}),
        new (winston.transports.File)({ filename: 'access.log',json: false})
    ]
})

//加载socket.io模块 监听指定端口
var http= require('http');
var io = require('socket.io').listen(port);
logger.info('service start');

//定义一个list存放uuid 每一个uuid对应一个socket id
var UUIDMap = {};
io.sockets.on('connection', function (socket) { 
    logger.info('connection:', socket.id);
    var UUID;
    //客户端进行uuid与socket.id绑定
    socket.on('register', function(data){
        var regUUID = data['uuid'];
        //如果存在 解决刷新二维码造成的uuid不一致问题
        if (UUID != null) {
            delete UUIDMap[UUID];
        }
        UUID = regUUID;
        UUIDMap[UUID] = socket.id;
        logger.info('save in UUIDMap', UUID);
    });
    
    //手机端确认登陆
    socket.on('confirm',function(data){      
        //提示web端 手机端扫码成功 并将token令牌返回
        replayToDisplayer(data, 'result');    
        //同时提示手机端 验证成功
        socket.emit('success', { result: 'success' })
        logger.info('mobile comfrim submit');
    });

    //客户端断开链接
    socket.on('disconnect', function () {
        logger.info('disconnect', socket.id);
        if (UUID != null) {
            logger.info('delete ', UUID);
            delete UUIDMap[UUID];
        }
    });
});

/**
 * 向指定web端发送信息
 * 
 * @param {json} data 要返回的数据
 * @param {string} event 要回调客户端的监听事件
 * @returns {undefined}
 */
function replayToDisplayer(data, event) {
    var submitUUID = data['uuid'];
    var displayerSocket = findSocketByUUID(submitUUID);

    if (displayerSocket != null) {
        logger.info('find socket and emit back ', submitUUID);
        displayerSocket.emit(event, data);
    } 
}

/**
 * 通过uuid查找socket connection id
 * 
 * @param {uuid} data 要返回的数据
 */
function findSocketByUUID(UUID) {
    var targetSocketID = UUIDMap[UUID];
    if (targetSocketID != null) {
        var targetSocket = io.sockets.connected[targetSocketID];
        if (targetSocket != null) {
            return targetSocket;
        }
        else {
            logger.info('cant find target socket by uuid: ', UUID);
        }
    }
    else {
        logger.error('cant find target socket id by uuid: ', UUID);
    }
    return null;
}

需要说的是,初始化链接后,每一个链接的客户端都对应一个socket的实例,即socket.id不同,我们也是通过这个将信息发送到指定的链接上去,node 的socket模块封装很好,我们只需要稍微加点业务逻辑进去即可,uuid可以理解为用户的标示,一个用户对于一个链接,我们可以通过用户标示找到socket.io链接


第二步是建立后端服务器用于生成二维码,生成uuid,验证token

这个很简单 如何生成二维码可以用谷歌的api  也可以用php的Qrcode包,网上有,而且很简单,不啰嗦了,实现代码如下:

<?php
include 'qrcode/phpqrcode.php';
//uuid 唯一的标示符 用于指定客户端收发信息
$uuid = 'abc123';
//生成二维码文件
$filename = 'qrcode'.time().mt_rand(1000,9999).'.png';
//二维码中包含的数据
$data = [
    "ip"=>'127.0.0.1',
    "port"=>'8888',
    'exprise'=>time()+60,
    'uuid'=>$uuid
];

try{
   QRcode::png('$data','temp/'.$filename,'L',15);
   echo json_encode(['code'=>1,'message'=>'temp/'.$filename,'uuid'=>$uuid]);
}catch(\Exception $e) {
   echo json_encode(['code'=>0,'message'=>$e->getMessage()]);
}

注意:多用户的情况下uuid一定要唯一 不重复 这很重要


第三步编写web端脚本index.html如下:

<span style="font-size:12px;"><!Doctype html>
<html> 
<head> 
  <title>扫码登录demo</title>
  <meta charset="utf-8"></meta>
</head> 
 
<body>
    <div style='margin:100px auto;width:80%;text-align:center'>
        <img src="" class="qrcode" style="display:none;margin:0 auto;"/><br />
        <p></p><br />
        <button class='button' onclick="getQrcode()" style='font-size:16px'>获取二维码登陆</button>
    </div>
<script type="text/javascript" src="js/socket.io.js"></script>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript">
    var timeLimit = 60;
    var uuid;
    /**
     *  获取二维码 
     */
    function getQrcode(){
        $.ajax({
            type: 'POST',
            url: 'index.php',
            data: {},
            dataType: 'json',
            success: function(data){
                if(data.code === 1){
                    $('.qrcode').attr('src',data.message);
                    $('.qrcode').show();
                    $('.button').attr('disabled',true);
                    uuid = data.uuid;
                    countDown();
                    init(data.uuid);
                    console.log('生成二维码成功,正在建立链接...');
                }else{
                    console.log(data.message);
                }
            }
         });
    }
    
    /**
     *  刷新计时器 
     */
    function countDown(){
        var id = setInterval(function (){
            var str = '二维码有效期剩余:'+timeLimit+'秒';
            $('.button').html(str);
            if(timeLimit >0){
                timeLimit--;
            }else{
                timeLimit = 60;
                clearInterval(id);
                $('.button').html('获取二维码登陆');
                $('.button').attr('disabled',false);
            }
        },1000);
    }
    
    /*
     *  初始化链接
     */
    function init(uuid) {
        var socket = io.connect('http://127.0.0.1:8888'); 
        //向服务器发送uuid绑定socket.id
        socket.emit('register',{uuid:uuid});
        //手机端扫码确认登陆后
        socket.on('result',function(data){
            //这里可以拿到data.token 拿到后就可以进行登陆了
            console.log("登陆成功"+'token='+data.token);
            //后续操作
        });
    } 
</script> 
</body>  
</html></span>

socket.io为客户端socket的实现脚本,你可以在cdnjs上下载到,我将它下载到本地了

第四部分为app端的模拟代码如下

<span style="font-size:12px;"><!Doctype html>
<html> 
<head> 
  <title>模拟app扫码验证demo</title>
  <meta charset="utf-8"></meta>
</head> 
 
<body>
    <div style='margin:100px auto;width:80%;text-align:center'>
        <p>注:现在模拟扫码的结果</p>
        <p>url:http://127.0.0.1</p>
        <p>port:8888</p>
        <p>uuid:abc123</p>
        <p>token:token</p>
        <div style='margin:0 auto;text-align:center' class="result">
            <p class="notice">扫码验证完毕!</p>
            <button onclick="confrimYes()">确认登陆</button>
        </div>
    </div>
<script type="text/javascript" src="js/socket.io.js"></script>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript">
    //这部分应该是扫码解析出来的 现在只做模拟
    var url = 'http://127.0.0.1';
    var port = 8888;
    var uuid = 'abc123';
    var token = 'token';
    serviceUrl = url + ':' +port;

    var socket = io.connect(serviceUrl);
    socket.on('success',function(data){      
         console.log("登陆成功"+'message'+data.result);
    });
    
    //确认登陆
    function confrimYes(){
        socket.emit('confirm',{uuid:uuid,token:token});
    }
    
</script> 
</body>  
</html></span>

最后是流程演示

1.开启服务node sokcet.io服务如下

php/socket.io实现扫码登录_第2张图片

2.通过浏览器访问index.html获取二维码 链接node service

php/socket.io实现扫码登录_第3张图片


socket链接可以通过控制台来查看
php/socket.io实现扫码登录_第4张图片

service端收到请求 建立连接

php/socket.io实现扫码登录_第5张图片

3.通过浏览器访问app.html模拟手机端授权

php/socket.io实现扫码登录_第6张图片

然后点击确认登陆  控制台输出登陆成功

4.完成扫码登录web端跳转


我们来看一下日志可以得到整个流程

php/socket.io实现扫码登录_第7张图片

第一行 service start 表示服务启动

第二行 connection  是web端扫码后进行链接

第三行表示收到web端发送的uuid 并保存

第四行是app链接

第五行输出了app 确认登陆 登陆成功

后边是链接断开信息

以上就是扫码登录的思路及实现demo 实质上也可以通过轮询来实现 轮询前后端反而更容易编写写

可以参考我的另一篇博文

http://blog.csdn.net/zhangsheng_1992/article/details/51291497

所有代码可以在这里找到

https://code.csdn.net/zhangsheng_1992/socket/tree/master


你可能感兴趣的:(PHP,Socket.IO,node,扫码登录)