跨域认证解决方案
数据结构:
头部
负载
签名
Header.Payload.Signature
/**
* 用户管理模块
*/
const router = require('koa-router')()
const User = require('./../models/userSchema')
const Counter = require('./../models/counterSchema')
const Menu = require('./../models/menuSchema')
const Role = require('./../models/roleSchema')
const util = require('./../utils/util')
const jwt = require('jsonwebtoken')
const md5 = require('md5')
router.prefix('/users')
// 用户登录
router.post('/login', async (ctx) => {
try {
const { userName, userPwd } = ctx.request.body;
/**
* 返回数据库指定字段,有三种方式
* 1. 'userId userName userEmail state role deptId roleList'
* 2. {userId:1,_id:0}
* 3. select('userId')
*/
const res = await User.findOne({
userName,
userPwd: md5(userPwd)
}, 'userId userName userEmail state role deptId roleList')
if (res) {
const data = res._doc;
const token = jwt.sign({
data
}, 'imooc', { expiresIn: '1h' })
data.token = token;
ctx.body = util.success(data)
} else {
ctx.body = util.fail("账号或密码不正确")
}
} catch (error) {
ctx.body = util.fail(error.msg)
}
})
// 用户列表
router.get('/list', async (ctx) => {
const { userId, userName, state } = ctx.request.query;
const { page, skipIndex } = util.pager(ctx.request.query)
let params = {}
if (userId) params.userId = userId;
if (userName) params.userName = userName;
if (state && state != '0') params.state = state;
try {
// 根据条件查询所有用户列表
const query = User.find(params, { _id: 0, userPwd: 0 })
const list = await query.skip(skipIndex).limit(page.pageSize)
const total = await User.countDocuments(params);
ctx.body = util.success({
page: {
...page,
total
},
list
})
} catch (error) {
ctx.body = util.fail(`查询异常:${error.stack}`)
}
})
// 获取全量用户列表
router.get('/all/list', async (ctx) => {
try {
const list = await User.find({}, "userId userName userEmail")
ctx.body = util.success(list)
} catch (error) {
ctx.body = util.fail(error.stack)
}
})
// 用户删除/批量删除
router.post('/delete', async (ctx) => {
// 待删除的用户Id数组
const { userIds } = ctx.request.body
// User.updateMany({ $or: [{ userId: 10001 }, { userId: 10002 }] })
const res = await User.updateMany({ userId: { $in: userIds } }, { state: 2 })
if (res.nModified) {
ctx.body = util.success(res, `共删除成功${res.nModified}条`)
return;
}
ctx.body = util.fail('删除失败');
})
// 用户新增/编辑
router.post('/operate', async (ctx) => {
const { userId, userName, userEmail, mobile, job, state, roleList, deptId, action } = ctx.request.body;
if (action == 'add') {
if (!userName || !userEmail || !deptId) {
ctx.body = util.fail('参数错误', util.CODE.PARAM_ERROR)
return;
}
const res = await User.findOne({ $or: [{ userName }, { userEmail }] }, '_id userName userEmail')
if (res) {
ctx.body = util.fail(`系统监测到有重复的用户,信息如下:${res.userName} - ${res.userEmail}`)
} else {
const doc = await Counter.findOneAndUpdate({ _id: 'userId' }, { $inc: { sequence_value: 1 } }, { new: true })
try {
const user = new User({
userId: doc.sequence_value,
userName,
userPwd: md5('123456'),
userEmail,
role: 1, //默认普通用户
roleList,
job,
state,
deptId,
mobile
})
user.save();
ctx.body = util.success('', '用户创建成功');
} catch (error) {
ctx.body = util.fail(error.stack, '用户创建失败');
}
}
} else {
if (!deptId) {
ctx.body = util.fail('部门不能为空', util.CODE.PARAM_ERROR)
return;
}
try {
const res = await User.findOneAndUpdate({ userId }, { mobile, job, state, roleList, deptId, })
ctx.body = util.success({}, '更新成功')
} catch (error) {
ctx.body = util.fail(error.stack, '更新失败')
}
}
})
// 获取用户对应的权限菜单
router.get("/getPermissionList", async (ctx) => {
let authorization = ctx.request.headers.authorization
let { data } = util.decoded(authorization)
let menuList = await getMenuList(data.role, data.roleList);
let actionList = getAction(JSON.parse(JSON.stringify(menuList)))
ctx.body = util.success({ menuList, actionList });
})
async function getMenuList(userRole, roleKeys) {
let rootList = []
if (userRole == 0) {
rootList = await Menu.find({}) || []
} else {
// 根据用户拥有的角色,获取权限列表
// 现查找用户对应的角色有哪些
let roleList = await Role.find({ _id: { $in: roleKeys } })
let permissionList = []
roleList.map(role => {
let { checkedKeys, halfCheckedKeys } = role.permissionList;
permissionList = permissionList.concat([...checkedKeys, ...halfCheckedKeys])
})
permissionList = [...new Set(permissionList)]
rootList = await Menu.find({ _id: { $in: permissionList } })
}
return util.getTreeMenu(rootList, null, [])
}
function getAction(list) {
let actionList = []
const deep = (arr) => {
while (arr.length) {
let item = arr.pop();
if (item.action) {
item.action.map(action => {
actionList.push(action.menuCode)
})
}
if (item.children && !item.action) {
deep(item.children)
}
}
}
deep(list)
return actionList;
}
module.exports = router
axios二次封装
/**
* axios二次封装
*/
import axios from 'axios'
import config from './../config'
import { ElMessage } from 'element-plus'
import router from './../router'
import storage from './storage'
const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求异常,请稍后重试'
// 创建axios实例对象,添加全局配置
const service = axios.create({
baseURL: config.baseApi,
timeout: 8000
})
// 请求拦截
service.interceptors.request.use((req) => {
const headers = req.headers;
const { token = "" } = storage.getItem('userInfo') || {};
if (!headers.Authorization) headers.Authorization = 'Bearer ' + token;
return req;
})
// 响应拦截
service.interceptors.response.use((res) => {
const { code, data, msg } = res.data;
if (code === 200) {
return data;
} else if (code === 500001) {
ElMessage.error(TOKEN_INVALID)
setTimeout(() => {
router.push('/login')
}, 1500)
return Promise.reject(TOKEN_INVALID)
} else {
ElMessage.error(msg || NETWORK_ERROR)
return Promise.reject(msg || NETWORK_ERROR)
}
})
/**
* 请求核心函数
* @param {*} options 请求配置
*/
function request(options) {
options.method = options.method || 'get'
if (options.method.toLowerCase() === 'get') {
options.params = options.data;
}
let isMock = config.mock;
if (typeof options.mock != 'undefined') {
isMock = options.mock;
}
if (config.env === 'prod') {
service.defaults.baseURL = config.baseApi
} else {
service.defaults.baseURL = isMock ? config.mockApi : config.baseApi
}
return service(options)
}
['get', 'post', 'put', 'delete', 'patch'].forEach((item) => {
request[item] = (url, data, options) => {
return request({
url,
data,
method: item,
...options
})
}
})
export default request;
服务端生成token客户端携带者下次请求加上去
const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')
const log4js = require('./utils/log4j')
const router = require('koa-router')()
const jwt = require('jsonwebtoken')
const koajwt = require('koa-jwt')
const util = require('./utils/util')
const users = require('./routes/users')
const menus = require('./routes/menus')
const roles = require('./routes/roles')
const depts = require('./routes/depts')
const leave = require('./routes/leave')
// error handler
onerror(app)
require('./config/db')
// middlewares
app.use(bodyparser({
enableTypes: ['json', 'form', 'text']
}))
app.use(json())
app.use(logger())
app.use(require('koa-static')(__dirname + '/public'))
app.use(views(__dirname + '/views', {
extension: 'pug'
}))
// logger
app.use(async (ctx, next) => {
log4js.info(`get params:${JSON.stringify(ctx.request.query)}`)
log4js.info(`post params:${JSON.stringify(ctx.request.body)}`)
await next().catch((err) => {
if (err.status == '401') {
ctx.status = 200;
ctx.body = util.fail('Token认证失败', util.CODE.AUTH_ERROR)
} else {
throw err;
}
})
})
app.use(koajwt({ secret: 'imooc' }).unless({
path: [/^/api/users/login/]
}))
router.prefix("/api")
router.use(users.routes(), users.allowedMethods())
router.use(menus.routes(), menus.allowedMethods())
router.use(roles.routes(), roles.allowedMethods())
router.use(depts.routes(), depts.allowedMethods())
router.use(leave.routes(), leave.allowedMethods())
app.use(router.routes(), router.allowedMethods())
// error-handling
app.on('error', (err, ctx) => {
log4js.error(`${err.stack}`)
});
module.exports = app