Files
niumalll/backend/routes/auth.js

292 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const Joi = require('joi')
const router = express.Router()
// 引入数据库模型
const { Admin } = require('../models')
// 引入认证中间件
const { authenticateJWT } = require('../middleware/auth')
/**
* @swagger
* components:
* schemas:
* LoginRequest:
* type: object
* required:
* - username
* - password
* properties:
* username:
* type: string
* description: 用户名
* password:
* type: string
* description: 密码
* LoginResponse:
* type: object
* properties:
* success:
* type: boolean
* message:
* type: string
* token:
* type: string
* user:
* type: object
* properties:
* id:
* type: integer
* username:
* type: string
* email:
* type: string
* user_type:
* type: string
* status:
* type: string
*/
// 从环境变量或配置中获取JWT密钥
const JWT_SECRET = process.env.JWT_SECRET || 'your_jwt_secret_key'
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '24h'
// 验证模式
const loginSchema = Joi.object({
username: Joi.string().required(),
password: Joi.string().required()
})
// 生成JWT令牌
const generateToken = (user) => {
return jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
user_type: user.user_type
},
JWT_SECRET,
{
expiresIn: JWT_EXPIRES_IN
}
)
}
/**
* @swagger
* /api/auth/login:
* post:
* summary: 用户登录
* tags: [认证管理]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoginRequest'
* responses:
* 200:
* description: 登录成功
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoginResponse'
* 400:
* description: 参数验证失败或用户名密码错误
* 401:
* description: 未授权或用户被禁用
* 500:
* description: 服务器内部错误
*/
// 用户登录
router.post('/login', async (req, res) => {
try {
// 参数验证
const { error, value } = loginSchema.validate(req.body)
if (error) {
return res.status(400).json({
success: false,
message: '参数验证失败',
details: error.details[0].message
})
}
const { username, password } = value
// 查找用户
const user = await Admin.findOne({
where: {
[Admin.sequelize.Op.or]: [
{ username },
{ email: username }
]
}
})
// 检查用户是否存在以及密码是否正确
if (!user || !(await bcrypt.compare(password, user.password_hash))) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
})
}
// 检查用户状态
if (user.status !== 'active') {
return res.status(401).json({
success: false,
message: '用户账号已被禁用'
})
}
// 生成JWT令牌
const token = generateToken(user)
// 准备返回的用户信息(不包含敏感数据)
const userInfo = {
id: user.id,
username: user.username,
email: user.email,
user_type: user.user_type,
status: user.status
}
res.json({
success: true,
message: '登录成功',
token,
user: userInfo
})
} catch (error) {
console.error('用户登录失败:', error)
res.status(500).json({
success: false,
message: '登录失败,请稍后再试'
})
}
})
/**
* @swagger
* /api/auth/me:
* get:
* summary: 获取当前用户信息
* tags: [认证管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: object
* properties:
* id:
* type: integer
* username:
* type: string
* email:
* type: string
* user_type:
* type: string
* status:
* type: string
* createdAt:
* type: string
* format: date-time
* updatedAt:
* type: string
* format: date-time
* 401:
* description: 未授权
* 500:
* description: 服务器内部错误
*/
// 获取当前用户信息
router.get('/me', authenticateJWT, async (req, res) => {
try {
const userId = req.user.id
// 根据ID查找用户
const user = await Admin.findByPk(userId, {
attributes: {
exclude: ['password_hash'] // 排除密码哈希等敏感信息
}
})
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
})
}
res.json({
success: true,
data: user
})
} catch (error) {
console.error('获取用户信息失败:', error)
res.status(500).json({
success: false,
message: '获取用户信息失败'
})
}
})
/**
* @swagger
* /api/auth/logout:
* post:
* summary: 用户登出
* tags: [认证管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 登出成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* message:
* type: string
* 401:
* description: 未授权
* 500:
* description: 服务器内部错误
*/
// 用户登出
router.post('/logout', authenticateJWT, async (req, res) => {
try {
// 注意JWT是无状态的服务器端无法直接使token失效
// 登出操作主要由客户端完成如删除本地存储的token
// 这里只返回成功信息
res.json({
success: true,
message: '登出成功'
})
} catch (error) {
console.error('用户登出失败:', error)
res.status(500).json({
success: false,
message: '登出失败,请稍后再试'
})
}
})
module.exports = router