301 lines
6.5 KiB
JavaScript
301 lines
6.5 KiB
JavaScript
|
|
/**
|
|||
|
|
* 认证控制器
|
|||
|
|
* @file authController.js
|
|||
|
|
* @description 处理用户认证相关的请求
|
|||
|
|
*/
|
|||
|
|
const jwt = require('jsonwebtoken');
|
|||
|
|
const { User, Role } = require('../models');
|
|||
|
|
const { validationResult } = require('express-validator');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户登录
|
|||
|
|
* @param {Object} req 请求对象
|
|||
|
|
* @param {Object} res 响应对象
|
|||
|
|
*/
|
|||
|
|
const login = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 验证请求参数
|
|||
|
|
const errors = validationResult(req);
|
|||
|
|
if (!errors.isEmpty()) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '请求参数错误',
|
|||
|
|
errors: errors.array()
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const { username, password } = req.body;
|
|||
|
|
|
|||
|
|
// 查找用户
|
|||
|
|
const user = await User.findOne({
|
|||
|
|
where: { username },
|
|||
|
|
include: [{
|
|||
|
|
model: Role,
|
|||
|
|
as: 'role'
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!user) {
|
|||
|
|
return res.status(401).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '用户名或密码错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查用户状态
|
|||
|
|
if (user.status !== 'active') {
|
|||
|
|
return res.status(401).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '账户已被禁用,请联系管理员'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证密码
|
|||
|
|
const isValidPassword = await user.validPassword(password);
|
|||
|
|
if (!isValidPassword) {
|
|||
|
|
// 增加登录失败次数
|
|||
|
|
user.login_attempts += 1;
|
|||
|
|
if (user.login_attempts >= 5) {
|
|||
|
|
user.status = 'locked';
|
|||
|
|
user.locked_until = new Date(Date.now() + 30 * 60 * 1000); // 锁定30分钟
|
|||
|
|
}
|
|||
|
|
await user.save();
|
|||
|
|
|
|||
|
|
return res.status(401).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '用户名或密码错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重置登录失败次数
|
|||
|
|
user.login_attempts = 0;
|
|||
|
|
user.locked_until = null;
|
|||
|
|
user.last_login = new Date();
|
|||
|
|
await user.save();
|
|||
|
|
|
|||
|
|
// 生成JWT令牌
|
|||
|
|
const token = jwt.sign(
|
|||
|
|
{
|
|||
|
|
userId: user.id,
|
|||
|
|
username: user.username,
|
|||
|
|
role: user.role?.name || 'user'
|
|||
|
|
},
|
|||
|
|
process.env.JWT_SECRET || 'your_jwt_secret_key_here',
|
|||
|
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 返回成功响应
|
|||
|
|
res.json({
|
|||
|
|
success: true,
|
|||
|
|
message: '登录成功',
|
|||
|
|
data: {
|
|||
|
|
token,
|
|||
|
|
user: {
|
|||
|
|
id: user.id,
|
|||
|
|
username: user.username,
|
|||
|
|
name: user.real_name,
|
|||
|
|
email: user.email,
|
|||
|
|
phone: user.phone,
|
|||
|
|
role: user.role?.name || 'user',
|
|||
|
|
avatar: user.avatar,
|
|||
|
|
lastLogin: user.last_login
|
|||
|
|
},
|
|||
|
|
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('登录错误:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '服务器内部错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户登出
|
|||
|
|
* @param {Object} req 请求对象
|
|||
|
|
* @param {Object} res 响应对象
|
|||
|
|
*/
|
|||
|
|
const logout = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
// 在实际应用中,可以将token加入黑名单
|
|||
|
|
res.json({
|
|||
|
|
success: true,
|
|||
|
|
message: '登出成功'
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('登出错误:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '服务器内部错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 刷新令牌
|
|||
|
|
* @param {Object} req 请求对象
|
|||
|
|
* @param {Object} res 响应对象
|
|||
|
|
*/
|
|||
|
|
const refreshToken = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { userId } = req.user;
|
|||
|
|
|
|||
|
|
// 查找用户
|
|||
|
|
const user = await User.findByPk(userId, {
|
|||
|
|
include: [{
|
|||
|
|
model: Role,
|
|||
|
|
as: 'role'
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!user || user.status !== 'active') {
|
|||
|
|
return res.status(401).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '用户不存在或已被禁用'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成新的JWT令牌
|
|||
|
|
const token = jwt.sign(
|
|||
|
|
{
|
|||
|
|
userId: user.id,
|
|||
|
|
username: user.username,
|
|||
|
|
role: user.role?.name || 'user'
|
|||
|
|
},
|
|||
|
|
process.env.JWT_SECRET || 'your_jwt_secret_key_here',
|
|||
|
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
success: true,
|
|||
|
|
message: '令牌刷新成功',
|
|||
|
|
data: {
|
|||
|
|
token,
|
|||
|
|
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('刷新令牌错误:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '服务器内部错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前用户信息
|
|||
|
|
* @param {Object} req 请求对象
|
|||
|
|
* @param {Object} res 响应对象
|
|||
|
|
*/
|
|||
|
|
const getCurrentUser = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { userId } = req.user;
|
|||
|
|
|
|||
|
|
// 查找用户
|
|||
|
|
const user = await User.findByPk(userId, {
|
|||
|
|
include: [{
|
|||
|
|
model: Role,
|
|||
|
|
as: 'role'
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!user) {
|
|||
|
|
return res.status(404).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '用户不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
success: true,
|
|||
|
|
message: '获取用户信息成功',
|
|||
|
|
data: {
|
|||
|
|
id: user.id,
|
|||
|
|
username: user.username,
|
|||
|
|
name: user.real_name,
|
|||
|
|
email: user.email,
|
|||
|
|
phone: user.phone,
|
|||
|
|
role: user.role?.name || 'user',
|
|||
|
|
avatar: user.avatar,
|
|||
|
|
status: user.status,
|
|||
|
|
lastLogin: user.last_login,
|
|||
|
|
createdAt: user.created_at
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户信息错误:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '服务器内部错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 修改密码
|
|||
|
|
* @param {Object} req 请求对象
|
|||
|
|
* @param {Object} res 响应对象
|
|||
|
|
*/
|
|||
|
|
const changePassword = async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const errors = validationResult(req);
|
|||
|
|
if (!errors.isEmpty()) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '请求参数错误',
|
|||
|
|
errors: errors.array()
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const { userId } = req.user;
|
|||
|
|
const { oldPassword, newPassword } = req.body;
|
|||
|
|
|
|||
|
|
// 查找用户
|
|||
|
|
const user = await User.findByPk(userId);
|
|||
|
|
if (!user) {
|
|||
|
|
return res.status(404).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '用户不存在'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证旧密码
|
|||
|
|
const isValidPassword = await user.validPassword(oldPassword);
|
|||
|
|
if (!isValidPassword) {
|
|||
|
|
return res.status(400).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '原密码错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新密码
|
|||
|
|
user.password = newPassword;
|
|||
|
|
await user.save();
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
success: true,
|
|||
|
|
message: '密码修改成功'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('修改密码错误:', error);
|
|||
|
|
res.status(500).json({
|
|||
|
|
success: false,
|
|||
|
|
message: '服务器内部错误'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
login,
|
|||
|
|
logout,
|
|||
|
|
refreshToken,
|
|||
|
|
getCurrentUser,
|
|||
|
|
changePassword
|
|||
|
|
};
|