Koa 2 + redis + mysql + Jwt + 后台管理

4 篇文章 0 订阅
1 篇文章 0 订阅

Koa + redis + mysql + Jwt + 后台管理

Koa解决跨域

// App.js 目录下
const app = new Koa()
const cors = require('koa2-cors'); //跨域处理

app.use(cors({
  credentials: true // 允许跨域带 cookie
})); 
//放到route前面

// logger
app.use(async (ctx, next) => {
  // 添加解决跨域
  ctx.set('Access-Control-Allow-Origin', '*');
  ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, authorization, Accept, X-Requested-With , yourHeaderFeild');
  ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
  ctx.set('Access-Control-Allow-Credentials', 'true')
  await next();
})

koa-morgan 日志处理

// morgan 处理日志
const ENV = process.env.NODE_ENV;
console.log(ENV)
if (ENV === 'dev') {
  app.use(morgan('dev'))  // 开发环境,打印在控制台
} else {  // 线上环境,写入文件
  const logFileName = path.join(__dirname, 'logs', 'access.log')
  const logStream = fs.createWriteStream(logFileName, { flags: 'a' })
  app.use(morgan('combined', {
    stream: logStream
  }))
}

JWT 权限控制

全局配置
const koaJwt = require('koa-jwt') //路由权限控制

//jwt
//秘钥
const jwtSecret = 'jwtSecret'
// token 失效返回 401
app.use(function(ctx, next){
    return next().catch((err) => {
        if (401 == err.statusCode ) {// token 失效
            ctx.code = 401;
            ctx.body = {
              code:401,
              data:null,
              message:"'token 失效请重新登录"
            };
        } 
        else {
            throw err;
        }
    });
});
// 免验证
app.use(koaJwt({secret:jwtSecret}).unless({
    path: ['/api/user/login','/api/user/register','/api/user/code'] //除了这些请求地址,其他的URL都需要验证
}))
JWT (方法库封装)
const jwt = require('jsonwebtoken'); // 用于签发、解析`token`
const { TOKEN_TIME } = require('../conf/db')
const jwtSecret = 'jwtSecret'
const tokenExpiresTime =  60 * 60 * 4 // 4小时
/* 获取一个期限为4小时的token */
function getToken(payload = {}) {
    return jwt.sign(payload, jwtSecret, { expiresIn: TOKEN_TIME  });
}

/* 通过token获取JWT的payload部分 */
function getJWTPayload(token) {
    // 验证并解析JWT
    return jwt.verify(token.split(' ')[1], jwtSecret);
}

/* 通过token获取JWT的payload部分 */
function getJWTPayComplete(token) {
    // 验证JWT
    return jwt.verify(token.split(' ')[1], jwtSecret, {
        complete: true
       });
}

function tokenVerify(token){
    if (token) {
        jwt.verify(token, jwtSecret, (err, decoded) => {
        if (err) {
            switch (err.name) {
            case 'JsonWebTokenError':
                res.status(403).send({ code: -1, msg: '无效的token' });
                break;
            case 'TokenExpiredError':
                res.status(403).send({ code: -1, msg: 'token过期' });
                break;
            }
        }
        })
    }
}


module.exports = {
    getToken,
    getJWTPayload,
    getJWTPayComplete
}

函数的调用
const { getToken , getJWTPayload} = require('../utils/jwt-token') // 获取token 和设置token

// 拿取数据加密生成token 
const token =  getToken({...userData,avatarName:userData.avatar_name, roles:role.role_id,job_id:job.job_id}) // token 加密

// 前端拿到 token 设置
if (getToken()) {
     // 记得带有空格
      config.headers.common['Authorization'] = 'Bearer ' + getToken()
 }

// 根据前端返回的token 返回用户信息
const token = context.header.authorization
const payload = getJWTPayload(token) // token解密


验证码
方法封装
const code = require("svg-captcha");

function createCode() {
    return code.create({
        size: 4,
        ignoreChars: "0o1iIl",
        noise: 3,
        color: true,
        background: "#fff",
        fontSize: 60
    });
    // const captcha = code.create(codeConfig);
    // this.ctx.session.verifCode = captcha.text.toLowerCase(); // 存session用于验证接口获取文字码
    // this.ctx.body = captcha.data;
}

module.exports = {
    createCode,
};

使用
router.get('/user/code',async (context,next)=>{
    let code = createCode();
    context.session.captcha = code.text.toLowerCase(); // 存session用于验证接口获取文字码
    context.body = new SuccessModel({img:(code.data),uuid:code.text.toLowerCase()},'获取code成功')
    // users

})

缓存session 、redis

全局配置
const session = require('koa-generic-session')
const redisStore = require('koa-redis')

// session 配置
app.keys = ['Jin_jin#'] //session的密码
app.use(session({
    cookie: {
        path: '/',
        httpOnly: true,
        // maxAge: 24  * 60 *  60  * 1000
        maxAge:  2 * 60 * 60 * 1000
    },
    // captcha:'',
    // secret:"dsafsafsf",//设置签名秘钥  内容可以任意填写
    // cookie:{maxAge:80*1000},//设置cookie的过期时间,例:80s后session和相应的cookie失效过期
    // resave:true,//强制保存,如果session没有被修改也要重新保存
    // saveUninitialized:false,//如果原先没有session那么久设置,否则不设置
    /* redis 配置
      REDIS_CONF = {
        host: '127.0.0.1',
        port: '6379',
        pwd:'123456'
    }
    */
    store: redisStore({
        // all: `${REDIS_CONF.host}:${REDIS_CONF.port}`,
        host:`${REDIS_CONF.host}`, 
        port:`${REDIS_CONF.port}`,
        pass:`${REDIS_CONF.pwd}`
    })
}))
redis 连接和使用(函数封装)
const { REDIS_CONF } = require('../conf/db');
/* redis 配置
  REDIS_CONF = {
    host: '127.0.0.1',
    port: '6379',
    pwd:'123456'
}
*/


//创建客户端
const Redis=require('ioredis');
// const redis=new Redis({
//    host : '127.0.0.1',//安装好的redis服务器地址
//    port : 80, //端口
//    prefix : 'sam:',//存诸前缀
//    password: 'auth',
//    ttl : 60 * 60 * 23,//过期时间
//    db: 0
// });
const redisClient = new Redis({host:REDIS_CONF.host,port:REDIS_CONF.port,
  password:REDIS_CONF.pwd
});

redisClient.on('error', err => {
  console.log(err);
});

redisClient.on('ready', res => {
  console.log('redis启动成功', res)
});

function set(key, val,time) {
  if (typeof val === 'object') {
    val = (val);
  }
  return redisClient.set(key, val,function(err,data){
    if(!err && time){
        // key   时间
        redisClient.expire(key,time, function (err1, data1) {
            console.log(data1)
        })
    }
   
  });

}

function get(key) {
  return redisClient.get(key,function(err,doc){
        return doc;
});;
}

// function delete(key){
//     return redisClient.getdel
// }

module.exports = {
  set,
  get
}
函数调用
const { get,set} = require('../utils/redis') // 缓存获取和设置

// 设置缓存 
const tokenFig = await set(`token_${userData.user_id}`,token,TOKEN_TIME)

// 异步获取缓存
const redisToken =  await get(`token_${userData.user_id}`)

路由控制(接口)

全局配置
接口编写
const router = require('koa-router')();
// 返回状态
const {SuccessModel,ErrorModel} = require('./../model/resModel')
// token
const { getToken , getJWTPayload} = require('../utils/jwt-token')
// 数据库返回 数据
const {menusRolesList} = require('./../controller/menu')

// 添加前缀
router.prefix('/api')

// 获取当前用户菜单
router.get('/menus/build',async (context,next)=>{
    const token = context.header.authorization
    const payload = getJWTPayload(token) // token解密
    const meuns_list = await menusRolesList(payload.user_id)
    context.body = new SuccessModel(meuns_list)
})
// 引入路由菜单
const user = require('./routes/user')
const menu = require('./routes/menu')

//  注册路由 router 
app.use(user.routes(), user.allowedMethods())
app.use(menu.routes(), menu.allowedMethods())

返回状态码
class BaseModel {
    constructor(data,message){
        if(typeof data === 'string'){
            this.message = data;
            data         = null;
            message      = null;
        }
        if(data){
            this.data = data;
        }
        if(message){
            this.message = message;
        }
    }
}

class SuccessModel extends BaseModel{
    constructor(data,message){
        super(data,message);
        this.code = 200
    }
}

class ErrorModel extends BaseModel{
    constructor(data,message){
        super(data,message);
        this.code = 500
    }
}

module.exports = {
    SuccessModel,
    ErrorModel
}

mysql 数据连接和操作

mysql 连接
const mysql = require('mysql');
const {MYSQL_CONF} = require('../conf/db');
/*
MYSQL_CONF = {
    host: '120.0.0.1',
    user: "root",
    password: "123456",
    port: "3306",
    database: "el-admin",
}
*/

// 创建 链接对象
const con = mysql.createConnection({
    ...MYSQL_CONF
});

// 开始链接
con.connect();

// // 添加执行 sql 函数
function exec(sql) {
    return new Promise((resolve,reject)=>{
        con.query(sql,(err,result)=>{
            if (err) {
                reject(err);
                return
            }
            resolve(result)
        })
    })
}

module.exports = {
    exec,
    escape: mysql.escape,
}

对数据库进行操作
const {exec} = require('../db/mysql')
// 获取role_id 权限路由
const menusRolesList = async (user_id)=>{
    const menu_all_sql = `SELECT title,pid,component,menu_sort,menu_id,icon,i_frame,path,cache,hidden,permission FROM sys_menu where menu_id in(select t.menu_id from (
        SELECT menu_id FROM sys_roles_menus WHERE role_id=(SELECT role_id FROM sys_users_roles WHERE user_id = ${user_id} )) as t)  ORDER BY  menu_sort`
    const menu_all = await exec(menu_all_sql); // 查询所以菜单 升序排列
    let power_arr =[]
    let findArray = []
    // 菜单权限数据查询
    menu_all.forEach(el => {
            power_arr.push(el)
            if(el.pid == null){
                let redirectObj ={
                    redirect:"noredirect",
                    path:`/${el.path}`,
                    name:`${el.title}`,
                    hidden:false,
                    component:'Layout',
                    alwaysShow:true,
                    meta:{
                        icon:el.icon,
                        title:el.title,
                        noCache:el.cache == 1 ? false : true
                    },
                    menu_id:el.menu_id,
                    children:[]


                }
                findArray.push(redirectObj)
            }
    });
    // 数据二次处理
    findArray.forEach(item=>{
        power_arr.forEach(el=>{
            if(item.menu_id == el.pid){
                // console.log(item.menu_id,'===',el.menu_id)
                let redirectObj ={
                    path:`${item.path}/${el.path}`,
                    name:`${el.title}`,
                    hidden:false,
                    component:el.component,
                    alwaysShow:false, // 是否有子集
                    meta:{
                        icon:el.icon,
                        title:el.title,
                        noCache:el.cache == 0 ? false : true
                    },
                }
                item.children.push(redirectObj)
            }
        })
    })
    
    return findArray
}
module.exports = {
    menusRolesList
}
数据调用
调用看路由控制里的(获取菜单管理)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值