Koa + redis + mysql + Jwt + 后台管理
文章目录
- Koa + redis + mysql + Jwt + 后台管理
- Koa解决跨域
- koa-morgan 日志处理
- JWT 权限控制
-
- 缓存session 、redis
- 全局配置
- redis 连接和使用(函数封装)
- 函数调用
- 路由控制(接口)
-
- mysql 数据连接和操作
-
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小时
function getToken(payload = {}) {
return jwt.sign(payload, jwtSecret, { expiresIn: TOKEN_TIME });
}
function getJWTPayload(token) {
// 验证并解析JWT
return jwt.verify(token.split(' ')[1], jwtSecret);
}
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那么久设置,否则不设置
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');
//创建客户端
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');
// 创建 链接对象
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
}
数据调用
调用看路由控制里的(获取菜单管理)