您的位置:首页 > 编程语言 > PHP开发

php/socket.io实现扫码登录

2016-05-07 22:59 471 查看
首先来先看张流程图,了解下原理



服务环境


后端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服务器的搭建非常简单,在我另一篇博文中有详细介绍,这里不多介绍,详见,/article/9187520.html
整个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服务如下






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







socket链接可以通过控制台来查看



service端收到请求 建立连接






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





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

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

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





第一行 service start 表示服务启动

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

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

第四行是app链接

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

后边是链接断开信息



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

可以参考我的另一篇博文

/article/9187519.html



所有代码可以在这里找到

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: