下面创建一个完整的 Node.js 后端服务,提供登录和列表接口,使用 Mock 数据模拟真实接口。我们将使用 Express 框架 + jsonwebtoken 做身份验证。
📁 项目结构
backend/
├── src/
│ ├── routes/
│ │ ├── auth.js # 认证路由
│ │ └── user.js # 用户路由
│ ├── middleware/
│ │ └── auth.js # JWT 认证中间件
│ ├── utils/
│ │ └── mockData.js # Mock 数据生成
│ └── app.js # 应用入口
├── .env # 环境变量
├── package.json
└── README.md
🚀 第一步:初始化项目
# 创建项目目录
mkdir backend
cd backend
# 初始化 package.json
npm init -y
# 安装核心依赖
npm install express cors dotenv jsonwebtoken bcryptjs
# 安装开发依赖
npm install nodemon –save-dev
📦 第二步:配置 package.json
{
"name": "backend",
"version": "1.0.0",
"description": "Node.js 后端服务 – 提供登录和列表接口",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
🔐 第三步:环境变量配置
创建 .env 文件:
# .env
PORT=3000
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
NODE_ENV=development
📊 第四步:生成 Mock 数据
创建 src/utils/mockData.js:
// src/utils/mockData.js
const bcrypt = require('bcryptjs')
// Mock 用户数据
const mockUsers = [
{
id: 1,
username: 'admin',
password: bcrypt.hashSync('123456', 10),
email: 'admin@example.com',
role: 'admin',
status: 1,
createTime: '2024-01-01 10:00:00',
lastLogin: '2024-01-15 14:30:00',
},
{
id: 2,
username: 'user1',
password: bcrypt.hashSync('123456', 10),
email: 'user1@example.com',
role: 'user',
status: 1,
createTime: '2024-01-02 11:00:00',
lastLogin: '2024-01-14 09:20:00',
},
{
id: 3,
username: 'user2',
password: bcrypt.hashSync('123456', 10),
email: 'user2@example.com',
role: 'user',
status: 0,
createTime: '2024-01-03 09:30:00',
lastLogin: '2024-01-10 16:45:00',
},
{
id: 4,
username: 'manager',
password: bcrypt.hashSync('123456', 10),
email: 'manager@example.com',
role: 'manager',
status: 1,
createTime: '2024-01-04 13:20:00',
lastLogin: '2024-01-13 11:10:00',
},
{
id: 5,
username: 'developer',
password: bcrypt.hashSync('123456', 10),
email: 'developer@example.com',
role: 'developer',
status: 1,
createTime: '2024-01-05 08:45:00',
lastLogin: '2024-01-12 17:00:00',
},
]
// Mock 用户列表数据(用于分页)
const generateUserList = (page = 1, size = 10, searchParams = {}) => {
// 过滤数据
let filteredUsers = […mockUsers]
// 关键词搜索(用户名或邮箱)
if (searchParams.keyword) {
const keyword = searchParams.keyword.toLowerCase()
filteredUsers = filteredUsers.filter(
user =>
user.username.toLowerCase().includes(keyword) ||
user.email.toLowerCase().includes(keyword)
)
}
// 状态过滤
if (searchParams.status !== undefined && searchParams.status !== '') {
filteredUsers = filteredUsers.filter(
user => user.status === Number(searchParams.status)
)
}
// 角色过滤
if (searchParams.role) {
filteredUsers = filteredUsers.filter(
user => user.role === searchParams.role
)
}
// 排序(按创建时间倒序)
filteredUsers.sort((a, b) => {
return new Date(b.createTime) – new Date(a.createTime)
})
// 计算总数
const total = filteredUsers.length
// 分页
const start = (page – 1) * size
const end = start + size
const rows = filteredUsers.slice(start, end)
return {
total,
rows,
page: Number(page),
size: Number(size),
totalPages: Math.ceil(total / size),
}
}
// 通过用户名查找用户
const findUserByUsername = (username) => {
return mockUsers.find(user => user.username === username)
}
// 通过 ID 查找用户
const findUserById = (id) => {
return mockUsers.find(user => user.id === Number(id))
}
module.exports = {
mockUsers,
generateUserList,
findUserByUsername,
findUserById,
}
🛡️ 第五步:JWT 认证中间件
创建 src/middleware/auth.js:
// src/middleware/auth.js
const jwt = require('jsonwebtoken')
// JWT 认证中间件
const authenticate = (req, res, next) => {
try {
// 1. 从请求头获取 token
const authHeader = req.headers.authorization
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
code: 401,
msg: '未登录或登录已过期',
data: null,
})
}
// 2. 提取 token
const token = authHeader.substring(7) // 去掉 'Bearer '
// 3. 验证 token
const secret = process.env.JWT_SECRET || 'your-secret-key'
const decoded = jwt.verify(token, secret)
// 4. 将用户信息挂载到 request 对象
req.user = decoded
next()
} catch (error) {
console.error('JWT 验证失败:', error)
// Token 过期或无效
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
code: 401,
msg: '登录已过期,请重新登录',
data: null,
})
}
return res.status(401).json({
code: 401,
msg: '无效的 Token',
data: null,
})
}
}
// 可选:角色权限校验
const authorize = (roles = []) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
code: 401,
msg: '未登录',
data: null,
})
}
// 如果 roles 为空,表示不需要特定角色
if (roles.length === 0) {
return next()
}
// 检查用户角色是否在允许列表中
if (!roles.includes(req.user.role)) {
return res.status(403).json({
code: 403,
msg: '没有权限访问该资源',
data: null,
})
}
next()
}
}
module.exports = {
authenticate,
authorize,
}
🛣️ 第六步:创建路由
1. 认证路由 (登录)
创建 src/routes/auth.js:
// src/routes/auth.js
const express = require('express')
const router = express.Router()
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const { findUserByUsername } = require('../utils/mockData')
/**
* 用户登录
* POST /api/auth/login
* Body: { username, password }
*/
router.post('/login', async (req, res) => {
try {
const { username, password } = req.body
// 1. 验证参数
if (!username || !password) {
return res.status(400).json({
code: 400,
msg: '用户名和密码不能为空',
data: null,
})
}
// 2. 查找用户
const user = findUserByUsername(username)
if (!user) {
return res.status(401).json({
code: 401,
msg: '用户名或密码错误',
data: null,
})
}
// 3. 验证密码
const isValidPassword = bcrypt.compareSync(password, user.password)
if (!isValidPassword) {
return res.status(401).json({
code: 401,
msg: '用户名或密码错误',
data: null,
})
}
// 4. 检查用户状态
if (user.status === 0) {
return res.status(403).json({
code: 403,
msg: '用户已被禁用,请联系管理员',
data: null,
})
}
// 5. 生成 JWT Token
const payload = {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
}
const secret = process.env.JWT_SECRET || 'your-secret-key'
const expiresIn = process.env.JWT_EXPIRES_IN || '7d'
const token = jwt.sign(payload, secret, { expiresIn })
// 6. 返回登录成功信息(不包含密码)
const userInfo = {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
status: user.status,
createTime: user.createTime,
lastLogin: new Date().toISOString().replace('T', ' ').substring(0, 19),
}
res.json({
code: 0,
msg: '登录成功',
data: {
token,
userInfo,
},
})
} catch (error) {
console.error('登录接口错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 验证 Token 是否有效
* GET /api/auth/verify
*/
router.get('/verify', (req, res) => {
try {
const authHeader = req.headers.authorization
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
code: 401,
msg: '未登录',
data: null,
})
}
const token = authHeader.substring(7)
const secret = process.env.JWT_SECRET || 'your-secret-key'
const decoded = jwt.verify(token, secret)
res.json({
code: 0,
msg: 'Token 有效',
data: decoded,
})
} catch (error) {
res.status(401).json({
code: 401,
msg: '无效的 Token',
data: null,
})
}
})
module.exports = router
2. 用户路由 (列表)
创建 src/routes/user.js:
// src/routes/user.js
const express = require('express')
const router = express.Router()
const { authenticate, authorize } = require('../middleware/auth')
const { generateUserList, findUserById } = require('../utils/mockData')
/**
* 获取用户列表(分页)
* GET /api/user/list
* Query: { page, size, keyword, status, role }
* 需要认证
*/
router.get('/list', authenticate, (req, res) => {
try {
// 1. 获取查询参数
const page = parseInt(req.query.page) || 1
const size = parseInt(req.query.size) || 10
const keyword = req.query.keyword || ''
const status = req.query.status !== undefined ? req.query.status : ''
const role = req.query.role || ''
// 2. 参数校验
if (page < 1) {
return res.status(400).json({
code: 400,
msg: '页码不能小于 1',
data: null,
})
}
if (size < 1 || size > 100) {
return res.status(400).json({
code: 400,
msg: '每页大小必须在 1-100 之间',
data: null,
})
}
// 3. 获取数据
const result = generateUserList(page, size, {
keyword,
status: status === '' ? undefined : status,
role,
})
// 4. 返回数据(不包含密码)
const rows = result.rows.map(user => {
const { password, …userWithoutPassword } = user
return userWithoutPassword
})
res.json({
code: 0,
msg: '获取用户列表成功',
data: {
total: result.total,
rows,
page: result.page,
size: result.size,
totalPages: result.totalPages,
},
})
} catch (error) {
console.error('获取用户列表错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 获取用户详情
* GET /api/user/:id
* 需要认证
*/
router.get('/:id', authenticate, (req, res) => {
try {
const id = parseInt(req.params.id)
if (isNaN(id)) {
return res.status(400).json({
code: 400,
msg: '无效的用户 ID',
data: null,
})
}
const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}
// 移除密码
const { password, …userInfo } = user
res.json({
code: 0,
msg: '获取用户详情成功',
data: userInfo,
})
} catch (error) {
console.error('获取用户详情错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 获取当前用户信息
* GET /api/user/info
* 需要认证
*/
router.get('/info', authenticate, (req, res) => {
try {
const userId = req.user.id
const user = findUserById(userId)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}
// 移除密码
const { password, …userInfo } = user
res.json({
code: 0,
msg: '获取用户信息成功',
data: userInfo,
})
} catch (error) {
console.error('获取用户信息错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 创建用户(需要管理员权限)
* POST /api/user
* 需要认证 + 管理员权限
*/
router.post('/', authenticate, authorize(['admin']), (req, res) => {
try {
const { username, email, role } = req.body
// 简单验证
if (!username || !email) {
return res.status(400).json({
code: 400,
msg: '用户名和邮箱不能为空',
data: null,
})
}
// 模拟创建成功
const newUser = {
id: Date.now(),
username,
email,
role: role || 'user',
status: 1,
createTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
lastLogin: null,
}
res.json({
code: 0,
msg: '创建用户成功',
data: newUser,
})
} catch (error) {
console.error('创建用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 更新用户(需要管理员权限)
* PUT /api/user/:id
* 需要认证 + 管理员权限
*/
router.put('/:id', authenticate, authorize(['admin']), (req, res) => {
try {
const id = parseInt(req.params.id)
const { email, role, status } = req.body
const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}
// 模拟更新成功
const updatedUser = {
…user,
email: email || user.email,
role: role || user.role,
status: status !== undefined ? status : user.status,
}
const { password, …userInfo } = updatedUser
res.json({
code: 0,
msg: '更新用户成功',
data: userInfo,
})
} catch (error) {
console.error('更新用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
/**
* 删除用户(需要管理员权限)
* DELETE /api/user/:id
* 需要认证 + 管理员权限
*/
router.delete('/:id', authenticate, authorize(['admin']), (req, res) => {
try {
const id = parseInt(req.params.id)
const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}
// 模拟删除成功
res.json({
code: 0,
msg: '删除用户成功',
data: null,
})
} catch (error) {
console.error('删除用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})
module.exports = router
🚀 第七步:创建应用入口
创建 src/app.js:
// src/app.js
const express = require('express')
const cors = require('cors')
require('dotenv').config()
// 导入路由
const authRoutes = require('./routes/auth')
const userRoutes = require('./routes/user')
// 创建 Express 应用
const app = express()
const PORT = process.env.PORT || 3000
// 中间件
app.use(cors()) // 允许跨域
app.use(express.json()) // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true })) // 解析 URL-encoded 请求体
// 日志中间件(开发环境)
if (process.env.NODE_ENV === 'development') {
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
next()
})
}
// 路由
app.use('/api/auth', authRoutes)
app.use('/api/user', userRoutes)
// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
})
})
// 404 处理
app.use((req, res) => {
res.status(404).json({
code: 404,
msg: '接口不存在',
data: null,
})
})
// 全局错误处理
app.use((err, req, res, next) => {
console.error('全局错误:', err)
res.status(500).json({
code: 500,
msg: err.message || '服务器内部错误',
data: null,
})
})
// 启动服务器
app.listen(PORT, () => {
console.log(`🚀 服务器已启动`)
console.log(`📍 地址: http://localhost:${PORT}`)
console.log(`🌿 环境: ${process.env.NODE_ENV || 'development'}`)
console.log('\\n📋 接口列表:')
console.log(` POST /api/auth/login – 用户登录`)
console.log(` GET /api/auth/verify – 验证 Token`)
console.log(` GET /api/user/list – 获取用户列表`)
console.log(` GET /api/user/:id – 获取用户详情`)
console.log(` GET /api/user/info – 获取当前用户信息`)
console.log(` POST /api/user – 创建用户`)
console.log(` PUT /api/user/:id – 更新用户`)
console.log(` DELETE /api/user/:id – 删除用户`)
console.log(` GET /health – 健康检查\\n`)
})
🧪 第八步:测试接口
1. 启动服务
npm run dev
2. 使用 curl 或 Postman 测试
测试登录接口
curl -X POST http://localhost:3000/api/auth/login \\
-H "Content-Type: application/json" \\
-d '{"username": "admin", "password": "123456"}'
响应示例:
{
"code": 0,
"msg": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIs…",
"userInfo": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"role": "admin",
"status": 1,
"createTime": "2024-01-01 10:00:00",
"lastLogin": "2024-01-15 14:30:00"
}
}
}
测试用户列表接口(需要 Token)
# 先获取 token,然后使用 token 访问
curl -X GET "http://localhost:3000/api/user/list?page=1&size=5" \\
-H "Authorization: Bearer YOUR_TOKEN_HERE"
响应示例:
{
"code": 0,
"msg": "获取用户列表成功",
"data": {
"total": 5,
"rows": [
{
"id": 5,
"username": "developer",
"email": "developer@example.com",
"role": "developer",
"status": 1,
"createTime": "2024-01-05 08:45:00",
"lastLogin": "2024-01-12 17:00:00"
}
],
"page": 1,
"size": 5,
"totalPages": 1
}
}
🔗 第九步:前端对接
更新前端的 API 配置:
// src/utils/axios.js
const service = axios.create({
baseURL: 'http://localhost:3000/api', // 修改为后端地址
timeout: 15000,
withCredentials: true,
})
前端 API 调用示例:
// src/api/modules/user.js
import request from '@/utils/axios'
// 用户登录
export function login(data) {
return request({
url: '/auth/login', // 注意:这里不需要 /api 前缀,因为 baseURL 已经包含
method: 'post',
data,
})
}
// 获取用户列表
export function getUserList(params) {
return request({
url: '/user/list',
method: 'get',
params,
})
}
📋 接口文档
| 用户登录 | POST | /api/auth/login | ❌ | 用户名密码登录,返回 Token |
| 验证 Token | GET | /api/auth/verify | ✅ | 验证 Token 是否有效 |
| 用户列表 | GET | /api/user/list | ✅ | 分页获取用户列表 |
| 用户详情 | GET | /api/user/:id | ✅ | 获取指定用户信息 |
| 当前用户信息 | GET | /api/user/info | ✅ | 获取当前登录用户信息 |
| 创建用户 | POST | /api/user | ✅ (admin) | 创建新用户 |
| 更新用户 | PUT | /api/user/:id | ✅ (admin) | 更新用户信息 |
| 删除用户 | DELETE | /api/user/:id | ✅ (admin) | 删除用户 |
🎯 高级功能(可选)
1. 添加日志记录
npm install morgan
// src/app.js
const morgan = require('morgan')
app.use(morgan('combined'))
2. 添加请求限流
npm install express-rate-limit
// src/app.js
const rateLimit = require('express-rate-limit')
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP 100个请求
})
app.use('/api', limiter)
3. 添加 Swagger 文档
npm install swagger-jsdoc swagger-ui-express
✅ 项目特点
这个后端服务可以直接用于前端开发,配合之前封装的 Axios 完美对接。所有数据都是 Mock 的,后续可以无缝替换为真实的数据库操作。


