As mentioned in interface Design of Small Program Cloud Function above, the TWO-DIMENSIONAL code is generated on the web management background interface, and then the user’s OpenID can be brought into the Web side by scanning the code with the small program, so that the Web and the small program can use the same user system.
The flow chart is as follows:
Specific steps and pseudocodes:
I. Generate two-dimensional code on the Web side:
The user to open the web backend interface, a web front end and web socket connection to the backend, web front-end generated qr code, the corresponding url such as http://xxx.com/loginByMiniprogram?uuid=xxxx, use socketId uuid for simplifying it, Of course, for the sake of security, you can also use the algorithm to generate a special SCAN UUID.
1.1 web – API
The Web terminal is only used by administrators, and the traffic is not large. Therefore, in order to simplify, the project does not use redis and other three-party cache, and the background can be run in a single process. After the app starts, a global socketClients variable is initialized to store and determine whether the socketId is out of date.
//app.js class AppBootHook { constructor (app) { this.app = app } async didLoad () { this.app.cache = {} / / initializes the global socketClients variables. This app. Cache. SocketClients = {}}}Copy the code
– Added egg-socket. IO support to config
//config/config.default.js
config.io = {
namespace: {
'/': {
connectionMiddleware: [],
packetMiddleware: []
}
}
}
Copy the code
Added a route to receive front-end socket messages in router.js
//router.js
module.exports = app => {
const { router, controller, io } = app
//...
io.route('hello', controller.socket.hello)
//....
}
Copy the code
Add a response to the front-end Hello message in SocketController, sending the UUID
//controller/socket.js
class SocketController extends Controller {
async hello () {
const { ctx, app } = this
const id = ctx.socket.id
const now = Date.now()
app.cache.socketClients[id] = now
ctx.socket.emit('uuid', id)
}
Copy the code
1.2 web front end
Initialize the socket on the login page, receive the UUID, and generate the URL
//login.vue <template> <! - omitted - > < qrcode: value = "url" : the options = "{width: 200}" v - if = "!!!!! url"></qrcode> <! -- omit --> </template> import IO from 'socket.io-client' import VueQrcode from '@chenfengyuan/vue-qrcode' export default { / /... data(){ uuid: '', }, computed: { url () { if (this.uuid) { return setQuery({ uuid: this.uuid }, `http://localhost:7001/loginByMiniprogram`) } return '' } }, created () { const self = this const socket = io('http://localhost:7001/') socket.on('connect', () => { socket.emit('hello') }) socket.on('uuid', (uuid) => { self.uuid = uuid }) this.socket = socket },Copy the code
Two, small program end scan code
2.1 Small program side
//demo. WXML <view> <button bindtap="scan">Copy the code
The small program side first calls scanCode, resolves the URL, calls the cloud function method loginQrCode, and puts the url of the two-dimensional code resolved as a parameter.
//demo.js scan: async function(){// Only allow to scan from the camera try{let {result: url} = await app.globalData.wxp.scanCode({ onlyFromCamera: true, }) if(url){ const {code, info} = await api.callCloud('loginQrCode',{url}) if(code==='0000'){ wx.showToast({ title: info, icon: 'success', duration: 2000 }) } } }catch(e){} },Copy the code
2.2 Cloud function side
The cloud function side will concatenate the parameters of openId, appId and other small programs only available in the cloud, and access the URL in get mode. Here, axios library is used as the CLIENT of HTTP. Note that because the small program only supports commonJS reference,.default should be added.
//cloudfunctions/runFunc/index.js const axios = require('axios').default async function main(event, context) { let {fname, version, token, clientType, adminOpenId, ... Opt} = event let result, role const {OPENID, APPID} = cloud.getwxContext () if(fname==='loginQrCode'){ Result = await db.collection('user'). Where ({openId: OPENID}).get() if(result.data && result.data.length){ result = await axios.get(event.url, {params:{openId: OPENID, appId: APPID, exist: '1', userName: result.data[0].userName, role: result.data[0].role }}) } else { result = await axios.get(event.url, {params:{openId: OPENID, appId: APPID, exist: } else{return {code:'0000', info: result.data}} else{return {code:'0610', info: result.data} } } //...Copy the code
3. The API side returns the login result to the Web front end according to the parameters of the GET request
3.1 The Web-API side processes GET requests
Add a route on the Web-API side
//router.js
router.get('/loginByMiniprogram', controller.adminusers.loginByMiniprogram)
Copy the code
Handles UUID verification in AdminUserscontroller, responds to cloud and Web front-end respectively
//app/controller/adminusers.js async loginByMiniprogram () { const { ctx, app } = this const { uuid, openId, role, userName, appId, exist } = ctx.request.query if (appId ! Return} const NSP = app.io. Of ('/') const id = uuid if (id &&) nsp.sockets[id]) { const time = app.cache.socketClients[id] if (time) { if (Date.now() - time > 60000 * 5) { nsp.sockets[id].emit('token', { code: '0603', info: Delete app.cache.socketClients[id]} else {const {err, data } = await ctx.service.adminusers.loginByMiniprogram({ uuid, openId, role, userName, exist }) if (err) { nsp.sockets[id].emit('token', { code: '0603', info: Err. Message | | 'users cannot login'})} else {NSP. Sockets [id]. Emit (' token '{code: '0000', data})} delete app.cache.socketClients[id] ctx.body = 'ctx.body'} else {ctx.body = 'invalid information ', } else {ctx.body = 'invalid message, invalid message'}}Copy the code
Verify uUID in AdminUsersService, generate token if valid
//service/adminusers.js async loginByMiniprogram ({ uuid, exist, openId, role = '', userName = '' }) { const { app } = this if (! App.cache. socketClients[uUID]) {return {err: new Error(' uUID is invalid ')}} if (exist! == '1') {return {err: new Error(' user does not exist ')}} if (role < 10) {return {err: New Error(' no admin permission ')}} const token = app.jwt.sign({openId, role: role) parseInt(role) }, app.config.jwt.secret, { expiresIn: '1d' }) return { err: null, data: { userName, token } } }Copy the code
3.2 The Web end receives the Token and performs login
//login.vue //... created(){ //... Socket. on('token', ({code, info, data}) => {if (code === '0000') {const {userName, token} = data, } else {// display error}})}Copy the code