Files
niumalll/backend/routes/auth.js

292 lines
6.9 KiB
JavaScript
Raw Normal View History

const express = require('express')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const Joi = require('joi')
const router = express.Router()
// 引入数据库模型
const { ApiUser } = 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 ApiUser.findOne({
where: {
[ApiUser.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 ApiUser.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