修改管理后台
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcrypt');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { User, Role, UserRole } = require('../models');
|
||||
const { User, Role, UserRole, Permission, MenuPermission } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const { verifyToken, checkRole } = require('../middleware/auth');
|
||||
const { loginAttemptsLimiter, recordLoginFailure, loginRateLimiter } = require('../middleware/security');
|
||||
const { getRolePermissions, getAccessibleMenus } = require('../config/permissions');
|
||||
|
||||
const router = express.Router();
|
||||
const { body, validationResult } = require('express-validator');
|
||||
@@ -117,6 +119,9 @@ const { body, validationResult } = require('express-validator');
|
||||
* description: 服务器错误
|
||||
*/
|
||||
router.post('/login',
|
||||
loginRateLimiter, // 登录频率限制
|
||||
loginAttemptsLimiter, // 登录失败次数限制
|
||||
recordLoginFailure, // 记录登录失败
|
||||
[
|
||||
body('username').notEmpty().withMessage('用户名不能为空'),
|
||||
body('password').isLength({ min: 6 }).withMessage('密码长度至少6位')
|
||||
@@ -145,15 +150,22 @@ router.post('/login',
|
||||
|
||||
let user;
|
||||
try {
|
||||
// 查找用户(根据用户名或邮箱)
|
||||
user = await User.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{ username: username },
|
||||
{ email: username }
|
||||
]
|
||||
}
|
||||
});
|
||||
// 查找用户(根据用户名或邮箱)
|
||||
console.log('查找用户:', username);
|
||||
user = await User.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{ username: username },
|
||||
{ email: username }
|
||||
]
|
||||
},
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'description']
|
||||
}]
|
||||
});
|
||||
console.log('查询结果:', user ? '用户找到' : '用户未找到');
|
||||
} catch (error) {
|
||||
console.log('数据库查询失败,使用测试用户数据');
|
||||
// 数据库连接失败时的测试用户数据
|
||||
@@ -182,21 +194,32 @@ router.post('/login',
|
||||
}
|
||||
|
||||
// 比较密码
|
||||
console.log('开始密码验证...');
|
||||
console.log('输入密码:', password);
|
||||
console.log('存储密码哈希:', user.password.substring(0, 30) + '...');
|
||||
|
||||
let isPasswordValid;
|
||||
if (user.password.startsWith('$2b$')) {
|
||||
if (user.password.startsWith('$2b$') || user.password.startsWith('$2a$')) {
|
||||
// 使用bcrypt比较
|
||||
console.log('使用bcrypt比较密码');
|
||||
isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
console.log('bcrypt比较结果:', isPasswordValid);
|
||||
} else {
|
||||
// 直接比较(用于测试数据)
|
||||
console.log('使用明文比较密码');
|
||||
isPasswordValid = password === user.password;
|
||||
console.log('明文比较结果:', isPasswordValid);
|
||||
}
|
||||
|
||||
if (!isPasswordValid) {
|
||||
console.log('密码验证失败');
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '用户名或密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
console.log('密码验证成功');
|
||||
|
||||
// 生成 JWT token
|
||||
const token = jwt.sign(
|
||||
@@ -216,11 +239,43 @@ router.post('/login',
|
||||
email: user.email
|
||||
};
|
||||
|
||||
// 获取用户权限信息 - 从数据库获取实际权限
|
||||
const userWithPermissions = await User.findByPk(user.id, {
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
include: [{
|
||||
model: Permission,
|
||||
as: 'permissions',
|
||||
through: { attributes: [] },
|
||||
attributes: ['permission_key']
|
||||
}, {
|
||||
model: MenuPermission,
|
||||
as: 'menuPermissions',
|
||||
through: { attributes: [] },
|
||||
attributes: ['menu_key']
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
// 从数据库获取功能权限
|
||||
const userPermissions = userWithPermissions.role && userWithPermissions.role.permissions
|
||||
? userWithPermissions.role.permissions.map(p => p.permission_key)
|
||||
: [];
|
||||
|
||||
// 从数据库获取菜单权限
|
||||
const accessibleMenus = userWithPermissions.role && userWithPermissions.role.menuPermissions
|
||||
? userWithPermissions.role.menuPermissions.map(m => m.menu_key)
|
||||
: [];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '登录成功',
|
||||
token,
|
||||
user: userData
|
||||
user: userData,
|
||||
role: user.role,
|
||||
permissions: userPermissions,
|
||||
accessibleMenus: accessibleMenus
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
@@ -443,9 +498,8 @@ router.get('/me', async (req, res) => {
|
||||
attributes: ['id', 'username', 'email'],
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'roles', // 添加as属性,指定关联别名
|
||||
attributes: ['name', 'description'],
|
||||
through: { attributes: [] } // 不包含中间表字段
|
||||
as: 'role', // 使用正确的关联别名
|
||||
attributes: ['name', 'description']
|
||||
}]
|
||||
});
|
||||
} catch (dbError) {
|
||||
@@ -527,135 +581,7 @@ router.get('/me', async (req, res) => {
|
||||
* 500:
|
||||
* description: 服务器错误
|
||||
*/
|
||||
router.get('/roles', async (req, res) => {
|
||||
try {
|
||||
// 用于测试500错误的参数
|
||||
if (req.query.forceError === 'true') {
|
||||
throw new Error('强制触发服务器错误');
|
||||
}
|
||||
// 为了测试,如果请求中包含test=true参数,则返回模拟数据
|
||||
if (req.query.test === 'true') {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
roles: [
|
||||
{ id: 0, name: 'string', description: 'string' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 为了测试403权限不足的情况
|
||||
if (req.query.testForbidden === 'true') {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: '权限不足'
|
||||
});
|
||||
}
|
||||
|
||||
// 从请求头获取token
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '未授权'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证token
|
||||
let decoded;
|
||||
try {
|
||||
decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
|
||||
} catch (err) {
|
||||
if (err instanceof jwt.JsonWebTokenError || err instanceof jwt.TokenExpiredError) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '未授权'
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是其他错误,返回模拟数据
|
||||
return res.json({
|
||||
success: true,
|
||||
roles: [
|
||||
{ id: 1, name: 'admin', description: '管理员' },
|
||||
{ id: 2, name: 'user', description: '普通用户' },
|
||||
{ id: 3, name: 'guest', description: '访客' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 检查用户角色
|
||||
let userRoles;
|
||||
try {
|
||||
const user = await User.findByPk(decoded.id, {
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'roles', // 添加as属性,指定关联别名
|
||||
attributes: ['name']
|
||||
}]
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户或角色不存在'
|
||||
});
|
||||
}
|
||||
|
||||
userRoles = user.roles.map(role => role.name);
|
||||
|
||||
// 检查用户是否具有admin角色
|
||||
if (!userRoles.includes('admin')) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: '权限不足'
|
||||
});
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.log('数据库连接失败,使用模拟数据');
|
||||
// 返回模拟数据
|
||||
return res.json({
|
||||
success: true,
|
||||
roles: [
|
||||
{ id: 1, name: 'admin', description: '管理员' },
|
||||
{ id: 2, name: 'user', description: '普通用户' },
|
||||
{ id: 3, name: 'guest', description: '访客' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 获取所有角色
|
||||
let roles;
|
||||
try {
|
||||
roles = await Role.findAll({
|
||||
attributes: ['id', 'name', 'description']
|
||||
});
|
||||
} catch (dbError) {
|
||||
console.log('数据库连接失败,使用模拟数据');
|
||||
// 返回模拟数据
|
||||
return res.json({
|
||||
success: true,
|
||||
roles: [
|
||||
{ id: 1, name: 'admin', description: '管理员' },
|
||||
{ id: 2, name: 'user', description: '普通用户' },
|
||||
{ id: 3, name: 'guest', description: '访客' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
roles
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取角色列表错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -1131,6 +1057,58 @@ router.delete('/users/:userId/roles/:roleId', async (req, res) => {
|
||||
* 401:
|
||||
* description: Token无效或已过期
|
||||
*/
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/roles:
|
||||
* get:
|
||||
* summary: 获取所有角色列表
|
||||
* tags: [Authentication]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取角色列表成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* data:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* name:
|
||||
* type: string
|
||||
* description:
|
||||
* type: string
|
||||
* 500:
|
||||
* description: 服务器错误
|
||||
*/
|
||||
router.get('/roles', async (req, res) => {
|
||||
try {
|
||||
const roles = await Role.findAll({
|
||||
attributes: ['id', 'name', 'description'],
|
||||
order: [['id', 'ASC']]
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: roles,
|
||||
roles: roles // 兼容性,同时提供两种格式
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取角色列表错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取角色列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/validate', verifyToken, async (req, res) => {
|
||||
try {
|
||||
// 如果能到达这里,说明token是有效的
|
||||
@@ -1138,7 +1116,7 @@ router.get('/validate', verifyToken, async (req, res) => {
|
||||
attributes: ['id', 'username', 'email', 'status'],
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'roles',
|
||||
as: 'role',
|
||||
attributes: ['id', 'name']
|
||||
}]
|
||||
});
|
||||
@@ -1150,6 +1128,10 @@ router.get('/validate', verifyToken, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取用户权限信息
|
||||
const userPermissions = user.role ? getRolePermissions(user.role.name) : [];
|
||||
const accessibleMenus = getAccessibleMenus(userPermissions);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Token有效',
|
||||
@@ -1158,7 +1140,9 @@ router.get('/validate', verifyToken, async (req, res) => {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
status: user.status,
|
||||
roles: user.roles
|
||||
role: user.role,
|
||||
permissions: userPermissions,
|
||||
accessibleMenus: accessibleMenus
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user