微信小程序后台开发之重新学习Koa(三)

135 阅读2分钟

前言

微信小程序后台开发之重新学习Koa(一)

微信小程序后台开发之重新学习Koa(二)

接上一篇

微信小程序授权code&权限

微信小程序授权code

登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见小程序登录。

services/ws.js

const util = require('util')
const axios = require('axios')
const { User } = require('@models/user')
const { generateToken } = require('@core/util')
const { Auth } = require('@middlewares/auth')

class WXManager {
    static async codeToToken(code) {
        const url = util.format(global.config.wx.loginUrl, global.config.wx.appID, global.config.wx.appSecret, code)

        const result = await axios.get(url)
        if(result.status !== 200) {
            throw new global.errs.AuthFailed('openid获取失败')
        }
        const errcode = result.data.errcode
        const errmsg = result.data.errmsg
        if(errcode) {
            throw new global.errs.AuthFailed('openid获取失败:' + errmsg)
        }

        let user = await User.getUserByOpenid({openid: result.data.openid})
        if(!user) {
            user = await User.registerByOpenid(result.data)
        }

        return generateToken(user.id, Auth.USER)
    }
}

module.exports = {
    WXManager
}

权限校验中间件

middlewares/auth.js

const basicAuth = require('basic-auth')
const jwt = require('jsonwebtoken')

class Auth {
    constructor(level) {
        this.level = level || 1
        Auth.USER = 8
        Auth.ADMIN = 16
        Auth.SUPER_ADMIN = 32
    }

    get m() {
        return async (ctx, next) => {
            const userToken = basicAuth(ctx.req)
            let errMsg = 'token不合法'

            if(!userToken || !userToken.name) {
                throw new global.errs.Forbidden(errMsg)
            }
            try {
                var decode = jwt.verify(userToken.name, global.config.security.secretKey)
            } catch (error) {
                if(error.name == 'TokenExpiredError') {
                    errMsg = 'token已过期'
                }
                throw new global.errs.Forbidden(errMsg)
            }

            if(decode.scope < this.level) {
                errMsg = '权限不足'
                throw new global.errs.Forbidden(errMsg)
            }

            ctx.auth = {
                uid: decode.uid,
                scope: decode.scope
            }

            await next()
        }
    }

    static verifyToken(token) {
        try {
            jwt.verify(token, global.config.security.secretKey)
            return true
        } catch (error) {
            return false
        }
    }
}

module.exports = {
    Auth
}

jwt token 处理

通过uid 和 用户权限标识生成token

const jwt = require('jsonwebtoken')

const generateToken = function (uid, scope) {
    const secretKey = global.config.security.secretKey
    const expiresIn = global.config.security.expiresIn
    const token = jwt.sign({
        uid,
        scope
    }, secretKey, {
        expiresIn
    })
    return token
}

在上面的class Auth中通过basicAuth获取请求头中的token,再通过 jwt.verify获取权限、判断token过期

 const userToken = basicAuth(ctx.req)
 var decode = jwt.verify(userToken.name, global.config.security.secretKey)
...
 try {
                var decode = jwt.verify(userToken.name, global.config.security.secretKey)
            } catch (error) {
                if(error.name == 'TokenExpiredError') {
                    errMsg = 'token已过期'
                }
                throw new global.errs.Forbidden(errMsg)
            }
...

new Auth 应用

使用中间件new Auth().m

还是拿bookapi为例

router.post('/getRecords', new Auth().m, getValidateParams('POST', getRecordSchema), async (ctx, next) => {
    const records = await Records.getRecords(ctx.auth.uid, ctx.request.body.type);
    ctx.body = records
})

sequelize 操作数据库

配置db.js

const Sequelize = require('sequelize')
const {
    dbName,
    host,
    port,
    user,
    password
} = require('../config/config').database

const sequelize = new Sequelize(dbName, user, password, {
    dialect: 'mysql',
    host,
    port,
    logging: true,
    timezone: '+08:00',
    define: {
        timestamps: true,
        paranoid: true,
        createdAt: 'created_at',
        updatedAt: 'updated_at',
        deletedAt: 'deleted_at',
        underscored: true
    }
})

sequelize.sync({
    force: false
})

module.exports = {
    sequelize
}

自动建表

require('module-alias/register')

const Koa = require('koa');
const path = require('path');
const bodyParser = require('koa-bodyparser');
const static = require('koa-static');

const catchError = require('@middlewares/exception');
const InitManager = require('@core/init');

// 自动建表
require('@models/index');

const app = new Koa();

// 全局错误处理
app.use(catchError);
// 使用ctx.body解析中间件 ctx.request.body
app.use(bodyParser());
// 静态资源
app.use(static(path.join(__dirname, './static')))
// 注册 中间键 上下文ctx 洋葱模型

// 异步 Promise
InitManager.initCore(app);

app.listen(3000, () => {
    console.log('启动 3000')
});

@models/index.js

require('./user');
require('./book');
require('./records');

Book Model

const { sequelize } = require('@core/db')

const { Sequelize, Model } = require('sequelize')

class Book extends Model {
    static async getBookByOpenid({openid, uid}) {
        const book = await Book.findOne({
            where: {
                openid,
                uid
            }
        })
        return book
    }
    static async createBook({openid, uid}) {
        const book = await Book.findOne({
            where: {
                openid,
                uid
            }
        })
        if(book) {
            throw new global.errs.AuthFailed('账本已存在')
        }
        return await Book.create({openid, uid})
    }
}

Book.init({
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    isDefault: {
        type: Sequelize.BOOLEAN,
        defaultValue: true
    },
    isAuto: {
        type: Sequelize.BOOLEAN,
        defaultValue: true
    },
    name: {
        type: Sequelize.STRING(64),
        defaultValue: '默认账本'
    },
    uid: {
        type: Sequelize.STRING(64),
        unique: true
    },
    openid: {
        type: Sequelize.STRING(64),
        unique: true
    },
    status: {
        type: Sequelize.INTEGER,
        defaultValue: 0
    }
}, {
    sequelize,
    tableName: 'book'
})

module.exports = {
    Book
}

到这里使用koa 基本就实现记账c端的api node代码已开源,可以提供给大家学习~欢迎指导

未完待续,持续更新中...

感谢关注点赞评论~