选题及构思

学的硬件,毕业设计如果做硬件实物又是一笔钱,最后选择了一个软件的题目,正好用来练手。
前后端基于JS的博客系统,用于完成毕业设计,初想使用koa作为后端,vue作为前端来完成,以后可能会改变

目录结构

第一部分先完成后端的koa路由、token授权、mysql操作类。


│ index.js
│ package-lock.json

├─config
│  config.js
│  sql.js
│  token.js

└─router
  article.js


以上为项目基础目录:index.js是入口文件;config为配置目录,其中的config.js主要是参数配置文件,sql.js为mysql的操作类,token.js生成token的方法、以及注册账号功能;router目录下的所有文件将自动加载为koa的路由,加载规则为:"/文件名/内置路由名",例如此例路由为:“/article/”。

程序代码


index.js文件:

const Koa = require("koa");//web服务库
const Router = require("koa-router");//koa路由
const Koapost = require("koa-body");//基于koa的post接收库
const jwt = require("jwt-simple");//json web token库
const config = require("./config/config");//配置文件
const jstSecret = config.jstSecret;//token密钥
const app = new Koa();
const router = new Router();

app.use(Koapost());//获得post信息

let token = require(__dirname + '/config/token.js');//加载登陆注册中间件
router.use('/user', token.routes(), token.allowedMethods());
app.use(router.routes());//挂载登陆注册中间件


app.use(async (ctx, next) => {
    if (ctx.request.header.authorization !== undefined) {
        let user = jwt.decode(ctx.request.header.authorization, jstSecret);
        if(Date.now() > user.exp){
            ctx.response.status = 403;
            ctx.response.body = {
                msg: "token已过期,请重新授权",
                "status": "-1"
            }
        }else{
            await next();
        }
    } else {
        ctx.response.status = 401;
        ctx.response.body = {
            msg: "非授权操作!",
            "status": "-1"
        }
    }
});

const fs = require("fs");
const urls = fs.readdirSync(__dirname + '/router');//获得所有子路由文件
urls.forEach(element => {//循环注册所有子路由
    let url = require(__dirname + '/router/' + element);
    router.use('/' + element.replace('.js', ''), url.routes(), url.allowedMethods());
});

app.use(router.routes());//把子路由挂载到koa之上
app.listen(3000);

config.js文件:

module.exports = {
    jstSecret: 'No permission',//jwt密钥
    tokenExpiresTime: 1000 * 60 * 60 * 24 * 7,//token过期时间
    mysql:{
        host: "No permission",//数据库地址
        port: "No permission",//端口
        user: "No permission",//用户名
        password: "No permission",//密码
        database: "No permission"//数据库名
    }
}

sql.js文件:

const mysql = require("mysql");//mysql操作库
const config = require("./config");//配置文件
const _db = Symbol("db");
class Sql {
    constructor() {
        this[_db] = mysql.createPool({
            host: config.mysql.host,
            port: config.mysql.port,
            user: config.mysql.user,
            password: config.mysql.password,
            database: config.mysql.database
        });
    }
    query(sql, value) {
        return new Promise((resove, reject) => {
            this[_db].getConnection((err, connection) => {
                if(err){
                    reject(err);
                }else{
                    connection.query(sql, value, (err, rows)=>{
                        if(err){
                            reject(err);
                        }else{
                            resove(rows);
                        }
                    })
                    connection.release();
                }
            })
        })
    }
}
module.exports = Sql;

token.js文件:

const Router = require("koa-router");//koa路由
const jwt = require("jwt-simple");//json web token库
const utility = require("utility");//sh1、md5加密库
const sql = require("./sql");//mysql封装类
const config = require("./config");//配置文件
const sqlQuery = new sql();
const token = new Router();
const tokenExpiresTime = config.tokenExpiresTime;//token超时时间
const jstSecret = config.jstSecret;//token密钥

token.post('/login', async ctx => {
    if(ctx.request.body.user === undefined || ctx.request.body.password === undefined){
        //检查是否存在post数据
        ctx.response.status = 400;
        ctx.response.body = {
            "msg": "登陆参数不正确!",
            "status": "-1"
        }
    }else{
        //验证账号密码的正确性
        let post_user = ctx.request.body.user;
        let post_password = utility.md5(utility.md5(ctx.request.body.password));
        let con = await sqlQuery.query("SELECT * FROM user WHERE name=? AND password=?", [post_user, post_password]);
        if(con.length === 1){
            const payload = {
                'sub': post_user,
                'exp': Date.now() + tokenExpiresTime
            };
            const token = jwt.encode(payload, jstSecret);
            ctx.response.status = 200;
            ctx.response.body = {
                "msg": "授权成功!",
                "user": post_user,
                "token": token,
                "status": "0"
            };
        }else{
            ctx.response.status = 200;
            ctx.response.body = {
                "msg": "账号或者密码错误!",
                "status": "-1"
            }
        }
    }
})
module.exports = token;

article.js文件:

const Router = require("koa-router");
const article = new Router();
article.get('/', async (ctx, next) => {
    ctx.response.status = 200;
    ctx.response.body = "article";
    await next();
});
article.get('/list', async (ctx, next) => {
    ctx.response.status = 200;
    ctx.response.body = "article/list";
    await next();
});
module.exports = article;

程序执行流程

  1. 运行index.js文件,先把Koapost()方法挂载到koa中间件上,它的作用是接收post过来的参数,可以用ctx.request.body来获得
  2. 加载config/token.js路由到koa中间件,这个中间站在第二个位置,它的作用是token授权;
  3. 接着就是整体路由监控,排除授权token接口外所有接口都要经过此接口,token验证失败时就会报错,验证成功才能进行其它操作。
  4. 然后循环读取router文件夹下的所有文件,把其中定义的路由全部加载到koa中间件下

整体框架大致如此,之后的文章将不会在贴代码,而只记录实现过程,代码将会托管在github中