清明假期在家无聊,写了一个微信小程序扫码,web登录的demo
大致的扫码流程就是如此。我呢做了一个基础版本。扫码,微信小程序redis绑定uuid和accessToken,再派发给web端,没有添加校验。
const qr = require("qr-image");
/**
* @summary 获取二维码
* @description 获取二维码
* @router get /api/v1/user/generateQrCode
* @request query integer size 大小 默认 1
* @request query integer margin 二维码周围间距默认1
* @request query string uuid 唯一标识
* @response 200 baseResponse successed
*/
async generateQrCode() {
const { ctx, service } = this;
const { size, margin, uuid } = ctx.query;
try {
// 大小默认5,二维码周围间距默认1
const img = qr.image(uuid || "", {
type: "png",
size: size || 5,
margin: margin || 1,
});
ctx.status = 200;
const result = {
img,
type: "image/png",
};
await service.redis.set(uuid, "", 300);
ctx.body = img
// ctx.body = ctx.helper.response({ data: result, msg: "生成成功" }); // 不能获取img对象赋值,直接src渲染
} catch (e) {
ctx.status = 414;
ctx.set("Content-Type", "text/html");
ctx.body = "ERROR
";
}
}
}
<img src="xxxx//api/v1/user/generateQrCode?uuid=xxx" />
官网链接
安装
$ npm i egg-socket.io --save
开启插件
// {app_root}/config/plugin.js
exports.io = {
enable: true,
package: 'egg-socket.io',
};
配置
// {app_root}/config/config.${env}.js
exports.io = {
init: { }, // passed to engine.io
namespace: {
'/': {
// connectionMiddleware是在client保持连接的时候调用的中间件
connectionMiddleware: [],
// packetMiddleware是在server发送包给client之后调用的中间件
packetMiddleware: [],
},
'/example': {
connectionMiddleware: [],
packetMiddleware: [],
},
},
};
socket.io规定文件夹
demo
├── app
│ ├── extend
│ │ └── helper.js
│ ├── io
│ │ ├── controller
│ │ │ └── default.js
│ │ └── middleware
│ │ ├── connection.js
│ │ └── packet.js
│ └── router.js
├── config
└── package.json
比如命名空间 " / "需要用到中间件 connection
// {app_root}/config/config.${env}.js
exports.io = {
init: { }, // passed to engine.io
namespace: {
'/': {
// connectionMiddleware是在client保持连接的时候调用的中间件
connectionMiddleware: ['connection'],
// packetMiddleware是在server发送包给client之后调用的中间件
packetMiddleware: [],
},
},
};
connection.js
const room = "default_room";
module.exports = () => {
return async (ctx, next) => {
// 权限校验通过
ctx.socket.emit("res", "auth success");
// 加入房间
ctx.socket.join(room);
ctx.socket.emit("dataState", {
a: 3,
}); // 测试
ctx.socket.on("checkQrCodeAndSaveAccessToken", async (data) => { // 绑定 uuid : accessToken 键值
if (data.qrCode) {
const exist = await ctx.service.redis.isExistKey(data.qrCode);
if (exist) {
await ctx.service.redis.set(data.qrCode, data.accessToken, 60);
} else {
ctx.socket.emit("qrCodeStatusFromRedis", { status: 0 });
}
}
});
ctx.socket.on("sendQrCodeForAccessToken", async (data) => { // 小程序确认,向web端派发accessToken
if (data.qrCode) {
const exist = await ctx.service.redis.isExistKey(data.qrCode);
if (exist) {
const accessToken = await ctx.service.redis.get(data.qrCode);
ctx.socket.emit("sendAccessToken", { accessToken });
} else {
ctx.socket.emit("qrCodeStatusFromRedis", { status: 0 });
}
}
});
// 放行
await next();
console.log("断开连接");
};
};
为了分离,方便维护代码
// {app_root}/app/router.js
module.exports = app => {
const { router, controller, io } = app;
router.get('/', controller.home.index);
// socket.io // 可以说成监听命名空间为"/" 上 exchange方法
io.of('/').route('exchange', io.controller.nsp.exchange);
};
客户端只需要
socket.emit('exchange', {});
框架是以 Cluster 方式启动的,而 socket.io 协议实现需要 sticky 特性支持,否则在多进程模式下无法正常工作。
由于 socket.io 的设计,在多进程中服务器必须在 sticky 模式下工作,故需要给 startCluster 传递 sticky 参数。
修改 package.json 中 npm scripts 脚本:
{
"scripts": {
"dev": "egg-bin dev --sticky",
"start": "egg-scripts start --sticky"
}
}
npm i weapp.socket.io
// 小程序端示例代码
const io = require('weapp.socket.io')
const socket = io('http://localhost:8005', {
transports: ['websocket'],
extraHeaders: {
token: wx.getStorageSync('token'), // 在这里扩展,header增加了token,socket校验,为了严谨。此处token跟二维码token无关
},
reconnectionAttempts: 3, // 失败后重新连接次数,一直失败总共尝试四次
reconnectionDelay: 2000, // 重新连接间隔时间毫秒
forceNew: true,
})
socket.on('connect', function () {
console.log('connected')
});
page({
openscanCode() { // 扫一扫事件
const that = this
wx.scanCode({
success(res) {
if (res.errMsg === 'scanCode:ok') {
const data = res.result
that.setData({
qrCode: data
})
socket.emit("checkQrCodeAndSaveAccessToken", {
qrCode: data,
accessToken: 'access_token' // 做测试
})
}
}
})
},
})
npm i vue-socket.io
注册
main.js
import VueSocketIO from 'vue-socket.io'
Vue.use(new VueSocketIO({
debug: true,
connection: 'http://127.0.0.1:8005/',
}))
页面
<template>
<div id="app">
<div>
<img alt="Vue logo" :src="url">
</div>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid';
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
data:{
url: 'http://127.0.0.1:8005/api/v1/user/generateQrCode?uuid=' + uuidv4()
}
components: {
HelloWorld
},
sockets: {
connect() {
console.log('链接成功');
},
dataState2: (data) => {
console.log(data,'data')
},
disconnect() {
console.log('断开链接')
},
reconnect() {
console.log('重新链接')
},
sendAccessToken(res) {
console.log('VueSocketIO', res.accessToken) // 调用用户信息接口
}
},
mounted() {
console.log(this)
// this.$socket.emit('chat', { subscribe: true }) // 事件派发
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
就这样基础版本的微信小程序扫码,web自动登录完成