first commit
This commit is contained in:
1
NodeServer/.pydio
Normal file
1
NodeServer/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
51645263-0a19-469f-811e-4b9ab5070e69
|
||||
1
NodeServer/BroadcastChannel/.pydio
Normal file
1
NodeServer/BroadcastChannel/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
adc12d86-cd98-4497-aa04-dd7b13c37945
|
||||
7
NodeServer/BroadcastChannel/index.js
Normal file
7
NodeServer/BroadcastChannel/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { BroadcastChannel } = require('broadcast-channel');
|
||||
|
||||
module.exports = {
|
||||
SubscribeExpired: new BroadcastChannel('SubscribeExpired'),
|
||||
SweepStatus: new BroadcastChannel('SweepStatus'),
|
||||
AuthorizedWeb: new BroadcastChannel('AuthorizedWeb')
|
||||
};
|
||||
33
NodeServer/app.js
Normal file
33
NodeServer/app.js
Normal file
@@ -0,0 +1,33 @@
|
||||
var createError = require('http-errors');
|
||||
var express = require('express');
|
||||
var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
|
||||
var indexRouter = require('./routes/index');
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use('/', indexRouter);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function(req, res, next) {
|
||||
next(createError(404));
|
||||
});
|
||||
|
||||
// error handler
|
||||
app.use(function(err, req, res, next) {
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
1
NodeServer/bin/.pydio
Normal file
1
NodeServer/bin/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
b6ceb8d6-c456-497e-91f8-db953297899f
|
||||
87
NodeServer/bin/www
Executable file
87
NodeServer/bin/www
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var app = require('../app');
|
||||
var debug = require('debug')(`nodeserver:server`);
|
||||
var http = require('http');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (port >= 0) {
|
||||
// port number
|
||||
return port;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
|
||||
function onListening() {
|
||||
var addr = server.address();
|
||||
console.log(`App Is Run: http://127.0.0.1:${addr.port}`)
|
||||
}
|
||||
1
NodeServer/mysql/.pydio
Normal file
1
NodeServer/mysql/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
254af4fe-8f9b-4edd-b6cf-893f0cc8185a
|
||||
21
NodeServer/mysql/index.js
Normal file
21
NodeServer/mysql/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const mysql = require("yn-mysql-utils");
|
||||
|
||||
class MSql {
|
||||
|
||||
constructor() {
|
||||
this.db = new mysql({
|
||||
host : '127.0.0.1',
|
||||
user : 'root',
|
||||
password : 'lb714500',
|
||||
database : 'ScanCodeLogin',
|
||||
connectionLimit : 10
|
||||
});
|
||||
}
|
||||
|
||||
async query (params) {
|
||||
return await this.db.Query({ sql: params.sql, par: params.data })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new MSql();
|
||||
22
NodeServer/package.json
Normal file
22
NodeServer/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "nodeserver",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node-dev ./bin/www",
|
||||
"socket": "node-dev ./routes/socket.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"broadcast-channel": "^3.1.0",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"express": "~4.16.1",
|
||||
"http-errors": "~1.6.3",
|
||||
"morgan": "~1.9.1",
|
||||
"qr-image": "^3.2.0",
|
||||
"redis": "^3.0.2",
|
||||
"socket.io": "^2.3.0",
|
||||
"string-random": "^0.1.3",
|
||||
"yn-mysql-utils": "^1.0.4"
|
||||
}
|
||||
}
|
||||
1
NodeServer/redis/.pydio
Normal file
1
NodeServer/redis/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
6fc9f101-e55b-4932-8cfc-af46363fd9da
|
||||
45
NodeServer/redis/index.js
Normal file
45
NodeServer/redis/index.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const redis = require("redis");
|
||||
const BroadcastChannel = require("../BroadcastChannel");
|
||||
|
||||
class Redis {
|
||||
constructor() {
|
||||
this.client = redis.createClient({
|
||||
host:'127.0.0.1',
|
||||
port:'6379',
|
||||
ttl: 5*60*1000
|
||||
});
|
||||
|
||||
this.client.on("error", function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
this.client.send_command('config', ['set', 'notify-keyspace-events', 'Ex'], this.SubscribeExpired);
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
return new Promise((resolve)=>{
|
||||
this.client.get(key, (err,res) => resolve(res));
|
||||
})
|
||||
}
|
||||
|
||||
set(key, value="") {
|
||||
this.client.set(key, value);
|
||||
// 设置key的过期时间60秒(其实也就是二维码的有效时间)
|
||||
this.client.expire(key, 60)
|
||||
}
|
||||
|
||||
SubscribeExpired(e, r) {
|
||||
let sub = redis.createClient(6379);
|
||||
const expired_subKey = '__keyevent@0__:expired';
|
||||
sub.subscribe(expired_subKey, function () {
|
||||
sub.on('message', function (chan, msg) {
|
||||
console.log(`二维码:${msg} 已超时过期... `);
|
||||
// 二维码失效,通过 BroadcastChannel 通知 routes/socket.js
|
||||
BroadcastChannel.SubscribeExpired.postMessage(msg);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Redis();
|
||||
|
||||
1
NodeServer/routes/.pydio
Normal file
1
NodeServer/routes/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
3fa0f904-8283-42e5-bf5b-a1b1c84cadc7
|
||||
129
NodeServer/routes/index.js
Normal file
129
NodeServer/routes/index.js
Normal file
@@ -0,0 +1,129 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const sql = require("../mysql");
|
||||
const redis = require("../redis");
|
||||
const BroadcastChannel = require("../BroadcastChannel");
|
||||
|
||||
/**
|
||||
* 用户已扫码但是未确认,这个接口只是为了通知web端,用户当前的进展状态
|
||||
*/
|
||||
router.get('/SweepStatus', async (req, res, next) => {
|
||||
let uuid = req.query.uuid;
|
||||
BroadcastChannel.SweepStatus.postMessage(uuid);
|
||||
res.json({ status: 200, msg: '发送成功', data: null })
|
||||
});
|
||||
|
||||
/**
|
||||
* 移动端App确认登录按钮点击时候的ajax处理
|
||||
*/
|
||||
router.get('/login', async (req, res, next) => {
|
||||
let uuid = req.query.uuid; // uuid
|
||||
let uid = req.query.uid; // 用户id
|
||||
|
||||
let RedisUid = await redis.get(uuid);
|
||||
if (RedisUid == null || RedisUid == undefined) {
|
||||
res.json({
|
||||
status: 404,
|
||||
msg: '二维码失效或不存在',
|
||||
data: null
|
||||
});
|
||||
} else {
|
||||
if (RedisUid.length !== 0) {
|
||||
res.json({
|
||||
status: 404,
|
||||
msg: '已登录,请勿重复扫码',
|
||||
data: null
|
||||
});
|
||||
} else {
|
||||
// 查询用户信息
|
||||
let user = (await sql.query({ sql: 'select * from user where id = ?', data: [ uid ] }))[0];
|
||||
// 保存用户数据到对应key的redis中
|
||||
redis.set(uuid, JSON.stringify(user));
|
||||
res.json({
|
||||
status: 200,
|
||||
msg: '扫码成功',
|
||||
data: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 被移动端App轮询的接口,主要让移动端知道,我移动端现在已登录的用户所对
|
||||
* 应的web端是否在请求让我授权登录,如果在web端在请求登录,那么移动端自
|
||||
* 动进入授权页面让用户授权,授权后结束轮询,否则一直轮询
|
||||
* PS: uniapp 无法连接到 Node.js 的 socket.io,所以只能用很low而且
|
||||
* 很消耗移动端资源的轮询了。等解决这个问题自然用socket重写这里实现
|
||||
*/
|
||||
router.get('/InquiryWebIsLogining', async (req, res, next) => {
|
||||
let uid = req.query.uid; // 用户id
|
||||
// console.log("uid: ", uid);
|
||||
let UserId = await redis.get(uid);
|
||||
// 没有查询到redis的 uid 数据的话
|
||||
if (UserId == null || UserId == undefined) {
|
||||
res.json({
|
||||
status: 404,
|
||||
msg: '没有对应的web端请求登录',
|
||||
data: null
|
||||
});
|
||||
} else {
|
||||
// UserId 有数值,但是为空,表示web请求登录,但移动端未授权
|
||||
if (UserId.length == 0) {
|
||||
res.json({
|
||||
status: 200,
|
||||
msg: 'web端正在请求授权',
|
||||
data: null
|
||||
});
|
||||
// UserId 有数值,但是不为空,表示web请求登录,移动端已授权,并且redis未超时
|
||||
} else {
|
||||
res.json({
|
||||
status: 500,
|
||||
msg: '授权成功,请勿重复授权',
|
||||
data: null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 授权web端登录的接口,主要被移动端App调用拿到移动端给的用户id,
|
||||
* 然后从数据库获取用户的详细数据,通过广播把用户数据通知给web端
|
||||
*/
|
||||
router.get('/AuthorizedWeb', async (req, res, next) => {
|
||||
let uid = req.query.uid; // 用户id
|
||||
let UserId = await redis.get(uid);
|
||||
|
||||
if (UserId == null || UserId == undefined) {
|
||||
res.json({
|
||||
status: 404,
|
||||
msg: '没有对应的web端请求登录',
|
||||
data: null
|
||||
});
|
||||
} else {
|
||||
// UserId 有数值,但是为空
|
||||
if (UserId.length == 0) {
|
||||
// 查询用户信息
|
||||
let user = (await sql.query({ sql: 'select * from user where id = ?', data: [ uid ] }))[0];
|
||||
// 保存用户数据到对应key的redis中
|
||||
redis.set(uid, JSON.stringify(user));
|
||||
BroadcastChannel.AuthorizedWeb.postMessage(user);
|
||||
res.json({
|
||||
status: 200,
|
||||
msg: '成功授权',
|
||||
data: null
|
||||
});
|
||||
|
||||
// UserId 有数值,但是不为空,表示web请求登录,移动端已授权,并且redis未超时
|
||||
} else {
|
||||
res.json({
|
||||
status: 404,
|
||||
msg: '授权成功,请勿重复授权',
|
||||
data: null
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
module.exports = router;
|
||||
68
NodeServer/routes/socket.js
Normal file
68
NodeServer/routes/socket.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const app = require('express')();
|
||||
const http = require('http').createServer(app);
|
||||
const io = require('socket.io')(http);
|
||||
const utils = require('../utils');
|
||||
const redis = require('../redis');
|
||||
const BroadcastChannel = require("../BroadcastChannel");
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
|
||||
// Web端获取二维码和uuid
|
||||
socket.on('getQRcode', () => {
|
||||
let uuid = utils.randomNumber(); // 生成uuid
|
||||
let QRcode = utils.getQRcode(uuid); // 根据uuid生成二维码的base64
|
||||
redis.set(uuid, ''); // 保存uuid 到redis中,但是用户数据留空,由移动端上传
|
||||
socket.emit('sendQRcode', { uuid: uuid, QRcode: QRcode }); // 向web端发送uuid和二维码base64数据
|
||||
});
|
||||
|
||||
/**
|
||||
* 检查移动端用户是否扫码二维码登录,如果扫码登录了
|
||||
* 那么二维码显示登录成功样式,并显示登录用户信息
|
||||
*/
|
||||
socket.on('checkScanCode', async key => {
|
||||
console.log(`Web端(Vue)在询问用户是否扫码:${key}...`);
|
||||
|
||||
let userInfo = await redis.get(key);
|
||||
// 如果key对应的value为空,那么用户未扫码
|
||||
// 反之 用户扫码并确定登录
|
||||
userInfo == null || userInfo.length == 0 ? socket.emit("waitScanCode") : socket.emit("SuccessScanCode", userInfo);
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 场景:用户已经移动端App扫码登录后,本地保存有用户的id
|
||||
* 功能:模仿微信web端发送请求,然后移动端授权web端的本次登录
|
||||
* web端请求移动端授权登录
|
||||
*/
|
||||
socket.on('WebRequestLogin', async id => {
|
||||
console.log("web端请求登录用户的id: ", id);
|
||||
let WebId = await redis.get(id);
|
||||
|
||||
// 如果redis中没有数值,表示web端还未开始请求移动端授权登录,那么保存id为redis key
|
||||
// 如果redis中有数值,表示web端用户已经在请求移动端授权登录,那么不重复保存id,并且提示web端
|
||||
if (WebId == null) {
|
||||
redis.set(id, ''); // 使用web端的用户id作为redis的key存储起来,value对应的数值由移动端填充
|
||||
} else {
|
||||
console.log("不等于null")
|
||||
socket.emit("DoNotRepeatRequest", '已发送登录请求,请在移动端授权本次登录')
|
||||
}
|
||||
});
|
||||
|
||||
// 接受redis key超时的广播通知,也就是用户长时间未扫二维码主要在redis/index.js中进行订阅key失效的事件
|
||||
// 然后广播通知被这里代码onmessage到,然后 socket 通知 Vue 前端
|
||||
BroadcastChannel.SubscribeExpired.onmessage = event => socket.emit("ScanCodeOvertime");
|
||||
|
||||
// 用户已扫码,但是未确认
|
||||
BroadcastChannel.SweepStatus.onmessage = event => socket.emit("ScanCodeing", event.data);
|
||||
|
||||
// 移动端成功授权web端
|
||||
BroadcastChannel.AuthorizedWeb.onmessage = function(event) {
|
||||
console.log("event: ", event);
|
||||
socket.emit("AuthorizedWebSuccess", event);
|
||||
};
|
||||
|
||||
socket.on('disconnect', () => console.log('web端用户掉线...'));
|
||||
|
||||
});
|
||||
|
||||
http.listen(9080, () => console.log('socket listening on *: 9080'));
|
||||
1
NodeServer/utils/.pydio
Normal file
1
NodeServer/utils/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
c5a72c45-7bb0-4bec-8631-53fb9b81e78f
|
||||
17
NodeServer/utils/index.js
Normal file
17
NodeServer/utils/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
var random = require('string-random');
|
||||
var qr = require('qr-image');
|
||||
|
||||
class Utils {
|
||||
|
||||
// 产随机字符串
|
||||
randomNumber() {
|
||||
return 'login/' + random(16, { numbers: true })
|
||||
}
|
||||
|
||||
// 根据 randomNumber 的随机随机字符串生成base64二维码
|
||||
getQRcode(str) {
|
||||
return `data:image/png;base64,${qr.imageSync(str,{ type:'png' }).toString("base64")}`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Utils();
|
||||
Reference in New Issue
Block a user