import { Request, Response } from 'express'; import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; import { prisma } from '../config/database'; import { logger } from '../utils/logger'; import { AuthRequest } from '../middleware/authMiddleware'; import type { Secret, SignOptions } from 'jsonwebtoken'; // Generate JWT token function generateToken(userId: string): string { const secret = "pandora"; if (!secret) { throw new Error('JWT_SECRET is not configured'); } const expiresIn = process.env.JWT_EXPIRES_IN || '7d'; return jwt.sign( { userId }, secret, { expiresIn: expiresIn as any } ); } // Create session async function createSession(userId: string, token: string, req: Request) { console.log('创建session:', { userId, token: token.substring(0, 20) + '...' }) const session = await prisma.session.create({ data: { userId, token, ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days }, }); console.log('Session创建成功:', { sessionId: session.id, expiresAt: session.expiresAt, currentTime: new Date() }) return session; } export const authController = { // 用户注册 async register(req: Request, res: Response) { const { username, password, confirmPassword } = req.body; // 验证密码确认 if (password !== confirmPassword) { return res.status(400).json({ error: '密码和确认密码不匹配' }); } // Check if user already exists const existingUser = await prisma.user.findUnique({ where: { username } }); if (existingUser) { return res.status(400).json({ error: '用户名已存在' }); } // Hash password const hashedPassword = await bcrypt.hash(password, parseInt(process.env.BCRYPT_ROUNDS || '12')); // Create user with isActive set to false by default const user = await prisma.user.create({ data: { username, password: hashedPassword, isActive: false, // 新注册用户默认为禁用状态 }, }); // Create audit log await prisma.auditLog.create({ data: { userId: user.id, action: 'USER_REGISTERED', resource: 'user', resourceId: user.id, ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); return res.status(201).json({ message: '注册成功,请等待管理员激活您的账户', user: { id: user.id, username: user.username, isAdmin: user.isAdmin, isActive: user.isActive, } }); }, // 用户登录 async login(req: Request, res: Response) { try { const { username, password } = req.body; console.log('登录请求:', { username, timestamp: new Date().toISOString() }); // Find user const user = await prisma.user.findUnique({ where: { username } }); console.log('查找用户结果:', { found: !!user, isActive: user?.isActive, userId: user?.id }); if (!user) { // 记录登录失败审计日志 await prisma.auditLog.create({ data: { userId: null, action: 'USER_LOGIN_FAILED', resource: 'user', resourceId: null, details: JSON.stringify({ username, reason: '用户不存在' }), ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); return res.status(401).json({ error: '用户不存在' }); } // Check if user is active if (!user.isActive) { // 记录登录失败审计日志 await prisma.auditLog.create({ data: { userId: user.id, action: 'USER_LOGIN_FAILED', resource: 'user', resourceId: user.id, details: JSON.stringify({ username, reason: '账户已被禁用' }), ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); return res.status(401).json({ error: '账户已被禁用,请联系管理员激活' }); } // Verify password console.log('开始验证密码...'); const isValidPassword = await bcrypt.compare(password, user.password); console.log('密码验证结果:', { isValid: isValidPassword }); if (!isValidPassword) { // 增加登录失败次数 const loginAttempts = (user.loginAttempts || 0) + 1; const updateData: any = { loginAttempts }; let userDisabled = false; // 如果失败次数达到5次,禁用账户 if (loginAttempts >= 5) { updateData.isActive = false; userDisabled = true; } await prisma.user.update({ where: { id: user.id }, data: updateData }); // 记录登录失败审计日志 await prisma.auditLog.create({ data: { userId: user.id, action: 'USER_LOGIN_FAILED', resource: 'user', resourceId: user.id, details: JSON.stringify({ username, reason: userDisabled ? '密码错误且账户已被禁用' : '密码错误', loginAttempts, isDisabled: userDisabled }), ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); if (userDisabled) { return res.status(401).json({ error: '登录失败次数过多,账户已被禁用,请联系管理员' }); } return res.status(401).json({ error: '用户名或密码错误', remainingAttempts: 5 - loginAttempts }); } // 登录成功,重置登录失败次数和锁定时间 await prisma.user.update({ where: { id: user.id }, data: { loginAttempts: 0, lockedUntil: null } }); // Generate token console.log('生成token...'); const token = generateToken(user.id); console.log('Token生成成功:', token.substring(0, 20) + '...'); // Delete existing sessions for this user (optional - for single session per user) console.log('删除用户现有sessions...'); await prisma.session.deleteMany({ where: { userId: user.id } }); // Create session console.log('创建新session...'); await createSession(user.id, token, req); // Update last login console.log('更新最后登录时间...'); await prisma.user.update({ where: { id: user.id }, data: { lastLoginAt: new Date() } }); // Create audit log await prisma.auditLog.create({ data: { userId: user.id, action: 'USER_LOGIN', resource: 'user', resourceId: user.id, ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); console.log('登录成功,返回响应...'); return res.json({ message: '登录成功', token, user: { id: user.id, username: user.username, isAdmin: user.isAdmin, } }); } catch (error) { console.error('登录过程中发生错误:', error); return res.status(500).json({ error: '服务器内部错误' }); } }, // 用户登出 async logout(req: Request, res: Response) { const token = req.headers.authorization?.substring(7); if (token) { // Delete session await prisma.session.deleteMany({ where: { token } }); } // Create audit log (if we have user info from token) try { if (token) { const decoded = jwt.verify(token, "pandora") as any; if (decoded && decoded.userId) { await prisma.auditLog.create({ data: { userId: decoded.userId, action: 'USER_LOGOUT', resource: 'user', resourceId: decoded.userId, ipAddress: (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress || null, userAgent: req.get('User-Agent') ?? null, } }); } } } catch (error) { // Token无效,不记录审计日志 console.log('登出时token无效,跳过审计日志记录'); } res.json({ message: '登出成功' }); }, // 获取当前用户信息 async getCurrentUser(req: AuthRequest, res: Response) { if (!req.user) { return res.status(401).json({ error: '未授权' }); } const user = await prisma.user.findUnique({ where: { id: req.user.id }, select: { id: true, username: true, isAdmin: true, isActive: true, lastLoginAt: true, createdAt: true, updatedAt: true, } }); if (!user) { return res.status(404).json({ error: '用户不存在' }); } return res.json({ user: user }); }, // 刷新token async refreshToken(req: Request, res: Response) { const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).json({ error: '刷新令牌是必需的' }); } try { const decoded = jwt.verify(refreshToken, "pandora") as any; const session = await prisma.session.findFirst({ where: { token: refreshToken, expiresAt: { gt: new Date() } }, include: { user: { select: { id: true, username: true, isAdmin: true, isActive: true } } } }); if (!session || !session.user || !session.user.isActive) { return res.status(401).json({ error: '无效的刷新令牌' }); } // Generate new token const newToken = generateToken(session.user.id); // Update session await prisma.session.update({ where: { id: session.id }, data: { token: newToken, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), } }); return res.json({ token: newToken, user: session.user }); } catch (error) { return res.status(401).json({ error: '无效的刷新令牌' }); } }, // 调试端点 - 检查用户session状态 async debugSession(req: AuthRequest, res: Response) { if (!req.user) { return res.status(401).json({ error: '未授权' }); } try { const sessions = await prisma.session.findMany({ where: { userId: req.user.id }, orderBy: { createdAt: 'desc' } }); return res.json({ userId: req.user.id, username: req.user.username, sessions: sessions.map((s: any) => ({ id: s.id, token: s.token.substring(0, 20) + '...', expiresAt: s.expiresAt, createdAt: s.createdAt, isExpired: s.expiresAt < new Date() })) }); } catch (error) { return res.status(500).json({ error: '获取session信息失败' }); } } };