Koa + redis + mysql + Jwt + 后台管理
Koa解决跨域
const app = new Koa()
const cors = require('koa2-cors');
app.use(cors({
credentials: true
}));
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 日志处理
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')
const jwtSecret = 'jwtSecret'
app.use(function(ctx, next){
return next().catch((err) => {
if (401 == err.statusCode ) {
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']
}))
JWT (方法库封装)
const jwt = require('jsonwebtoken');
const { TOKEN_TIME } = require('../conf/db')
const jwtSecret = 'jwtSecret'
const tokenExpiresTime = 60 * 60 * 4
function getToken(payload = {}) {
return jwt.sign(payload, jwtSecret, { expiresIn: TOKEN_TIME });
}
function getJWTPayload(token) {
return jwt.verify(token.split(' ')[1], jwtSecret);
}
function getJWTPayComplete(token) {
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')
const token = getToken({...userData,avatarName:userData.avatar_name, roles:role.role_id,job_id:job.job_id})
if (getToken()) {
config.headers.common['Authorization'] = 'Bearer ' + getToken()
}
const token = context.header.authorization
const payload = getJWTPayload(token)
验证码
方法封装
const code = require("svg-captcha");
function createCode() {
return code.create({
size: 4,
ignoreChars: "0o1iIl",
noise: 3,
color: true,
background: "#fff",
fontSize: 60
});
}
module.exports = {
createCode,
};
使用
router.get('/user/code',async (context,next)=>{
let code = createCode();
context.session.captcha = code.text.toLowerCase();
context.body = new SuccessModel({img:(code.data),uuid:code.text.toLowerCase()},'获取code成功')
})
缓存session 、redis
全局配置
const session = require('koa-generic-session')
const redisStore = require('koa-redis')
app.keys = ['Jin_jin#']
app.use(session({
cookie: {
path: '/',
httpOnly: true,
maxAge: 2 * 60 * 60 * 1000
},
store: redisStore({
host:`${REDIS_CONF.host}`,
port:`${REDIS_CONF.port}`,
pass:`${REDIS_CONF.pwd}`
})
}))
redis 连接和使用(函数封装)
const { REDIS_CONF } = require('../conf/db');
const Redis=require('ioredis');
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){
redisClient.expire(key,time, function (err1, data1) {
console.log(data1)
})
}
});
}
function get(key) {
return redisClient.get(key,function(err,doc){
return doc;
});;
}
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')
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)
const meuns_list = await menusRolesList(payload.user_id)
context.body = new SuccessModel(meuns_list)
})
const user = require('./routes/user')
const menu = require('./routes/menu')
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');
const con = mysql.createConnection({
...MYSQL_CONF
});
con.connect();
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')
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){
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
}
数据调用
调用看路由控制里的(获取菜单管理)