You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
8.8 KiB
402 lines
8.8 KiB
|
2 weeks ago
|
# 后端 API 实现参考
|
||
|
|
|
||
|
|
这是一个使用 Node.js + Express 的简单后端示例,展示如何实现登录接口。
|
||
|
|
|
||
|
|
## 快速开始 (Node.js + Express)
|
||
|
|
|
||
|
|
### 1. 初始化项目
|
||
|
|
|
||
|
|
```bash
|
||
|
|
mkdir admin-backend
|
||
|
|
cd admin-backend
|
||
|
|
npm init -y
|
||
|
|
npm install express cors jsonwebtoken bcryptjs dotenv
|
||
|
|
npm install -D nodemon typescript ts-node
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 创建 `server.js`
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const express = require('express')
|
||
|
|
const cors = require('cors')
|
||
|
|
const jwt = require('jsonwebtoken')
|
||
|
|
require('dotenv').config()
|
||
|
|
|
||
|
|
const app = express()
|
||
|
|
const PORT = process.env.PORT || 3000
|
||
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'
|
||
|
|
|
||
|
|
// 中间件
|
||
|
|
app.use(cors())
|
||
|
|
app.use(express.json())
|
||
|
|
|
||
|
|
// 模拟用户数据
|
||
|
|
const users = [
|
||
|
|
{
|
||
|
|
id: '1',
|
||
|
|
username: 'admin',
|
||
|
|
password: '123456', // 实际应该加密存储
|
||
|
|
email: 'admin@example.com',
|
||
|
|
role: 'admin'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: '2',
|
||
|
|
username: 'user',
|
||
|
|
password: '123456',
|
||
|
|
email: 'user@example.com',
|
||
|
|
role: 'user'
|
||
|
|
}
|
||
|
|
]
|
||
|
|
|
||
|
|
// 登录接口
|
||
|
|
app.post('/api/auth/login', (req, res) => {
|
||
|
|
const { username, password } = req.body
|
||
|
|
|
||
|
|
// 验证输入
|
||
|
|
if (!username || !password) {
|
||
|
|
return res.status(400).json({
|
||
|
|
code: 400,
|
||
|
|
message: '用户名和密码不能为空'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 查找用户
|
||
|
|
const user = users.find(u => u.username === username && u.password === password)
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return res.status(401).json({
|
||
|
|
code: 401,
|
||
|
|
message: '用户名或密码错误'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成 JWT token
|
||
|
|
const token = jwt.sign(
|
||
|
|
{ id: user.id, username: user.username, role: user.role },
|
||
|
|
JWT_SECRET,
|
||
|
|
{ expiresIn: '7d' }
|
||
|
|
)
|
||
|
|
|
||
|
|
// 返回成功响应
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '登录成功',
|
||
|
|
data: {
|
||
|
|
token,
|
||
|
|
user: {
|
||
|
|
id: user.id,
|
||
|
|
username: user.username,
|
||
|
|
email: user.email,
|
||
|
|
role: user.role
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
// 登出接口
|
||
|
|
app.post('/api/auth/logout', (req, res) => {
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '登出成功'
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
// 获取用户信息接口 (需要认证)
|
||
|
|
app.get('/api/auth/userinfo', authenticateToken, (req, res) => {
|
||
|
|
const user = users.find(u => u.id === req.user.id)
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return res.status(404).json({
|
||
|
|
code: 404,
|
||
|
|
message: '用户不存在'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: {
|
||
|
|
id: user.id,
|
||
|
|
username: user.username,
|
||
|
|
email: user.email,
|
||
|
|
role: user.role
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
// Token 验证中间件
|
||
|
|
function authenticateToken(req, res, next) {
|
||
|
|
const authHeader = req.headers['authorization']
|
||
|
|
const token = authHeader && authHeader.split(' ')[1] // Bearer <token>
|
||
|
|
|
||
|
|
if (!token) {
|
||
|
|
return res.status(401).json({
|
||
|
|
code: 401,
|
||
|
|
message: 'Token 缺失'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
jwt.verify(token, JWT_SECRET, (err, user) => {
|
||
|
|
if (err) {
|
||
|
|
return res.status(401).json({
|
||
|
|
code: 401,
|
||
|
|
message: 'Token 无效或已过期'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
req.user = user
|
||
|
|
next()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 启动服务器
|
||
|
|
app.listen(PORT, () => {
|
||
|
|
console.log(\`✅ 服务器运行在 http://localhost:\${PORT}\`)
|
||
|
|
console.log(\`📝 API 文档: http://localhost:\${PORT}/api/docs\`)
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 创建 `.env`
|
||
|
|
|
||
|
|
```env
|
||
|
|
PORT=3000
|
||
|
|
JWT_SECRET=your-super-secret-key-change-in-production
|
||
|
|
NODE_ENV=development
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. 运行服务器
|
||
|
|
|
||
|
|
```bash
|
||
|
|
node server.js
|
||
|
|
# 或使用 nodemon 自动重启
|
||
|
|
npx nodemon server.js
|
||
|
|
```
|
||
|
|
|
||
|
|
## API 端点说明
|
||
|
|
|
||
|
|
### 登录接口
|
||
|
|
|
||
|
|
**请求**
|
||
|
|
```
|
||
|
|
POST /api/auth/login
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"username": "admin",
|
||
|
|
"password": "123456"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**成功响应 (200)**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "登录成功",
|
||
|
|
"data": {
|
||
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
|
|
"user": {
|
||
|
|
"id": "1",
|
||
|
|
"username": "admin",
|
||
|
|
"email": "admin@example.com",
|
||
|
|
"role": "admin"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**错误响应 (401)**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"code": 401,
|
||
|
|
"message": "用户名或密码错误"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 获取用户信息接口
|
||
|
|
|
||
|
|
**请求**
|
||
|
|
```
|
||
|
|
GET /api/auth/userinfo
|
||
|
|
Authorization: Bearer <token>
|
||
|
|
```
|
||
|
|
|
||
|
|
**成功响应**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "获取成功",
|
||
|
|
"data": {
|
||
|
|
"id": "1",
|
||
|
|
"username": "admin",
|
||
|
|
"email": "admin@example.com",
|
||
|
|
"role": "admin"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 使用 cURL 测试
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 登录
|
||
|
|
curl -X POST http://localhost:3000/api/auth/login \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"username":"admin","password":"123456"}'
|
||
|
|
|
||
|
|
# 获取用户信息
|
||
|
|
curl -X GET http://localhost:3000/api/auth/userinfo \
|
||
|
|
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||
|
|
```
|
||
|
|
|
||
|
|
## 使用 Postman 测试
|
||
|
|
|
||
|
|
1. 打开 Postman
|
||
|
|
2. 创建 POST 请求到 `http://localhost:3000/api/auth/login`
|
||
|
|
3. 在 Body → raw → JSON 中输入:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"username": "admin",
|
||
|
|
"password": "123456"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
4. 点击 Send
|
||
|
|
5. 复制响应中的 token
|
||
|
|
|
||
|
|
## Python Flask 示例
|
||
|
|
|
||
|
|
```python
|
||
|
|
from flask import Flask, request, jsonify
|
||
|
|
from flask_cors import CORS
|
||
|
|
from functools import wraps
|
||
|
|
import jwt
|
||
|
|
from datetime import datetime, timedelta
|
||
|
|
|
||
|
|
app = Flask(__name__)
|
||
|
|
CORS(app)
|
||
|
|
app.config['JSON_AS_ASCII'] = False
|
||
|
|
SECRET_KEY = 'your-secret-key'
|
||
|
|
|
||
|
|
# 模拟用户数据
|
||
|
|
USERS = [
|
||
|
|
{'id': '1', 'username': 'admin', 'password': '123456', 'email': 'admin@example.com', 'role': 'admin'},
|
||
|
|
{'id': '2', 'username': 'user', 'password': '123456', 'email': 'user@example.com', 'role': 'user'}
|
||
|
|
]
|
||
|
|
|
||
|
|
def token_required(f):
|
||
|
|
@wraps(f)
|
||
|
|
def decorated(*args, **kwargs):
|
||
|
|
token = request.headers.get('Authorization', '').replace('Bearer ', '')
|
||
|
|
if not token:
|
||
|
|
return jsonify({'code': 401, 'message': 'Token 缺失'}), 401
|
||
|
|
try:
|
||
|
|
data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
|
||
|
|
request.user_id = data['id']
|
||
|
|
except:
|
||
|
|
return jsonify({'code': 401, 'message': 'Token 无效'}), 401
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
return decorated
|
||
|
|
|
||
|
|
@app.route('/api/auth/login', methods=['POST'])
|
||
|
|
def login():
|
||
|
|
data = request.json
|
||
|
|
username = data.get('username')
|
||
|
|
password = data.get('password')
|
||
|
|
|
||
|
|
if not username or not password:
|
||
|
|
return jsonify({'code': 400, 'message': '用户名和密码不能为空'}), 400
|
||
|
|
|
||
|
|
user = next((u for u in USERS if u['username'] == username and u['password'] == password), None)
|
||
|
|
|
||
|
|
if not user:
|
||
|
|
return jsonify({'code': 401, 'message': '用户名或密码错误'}), 401
|
||
|
|
|
||
|
|
token = jwt.encode(
|
||
|
|
{'id': user['id'], 'username': user['username']},
|
||
|
|
SECRET_KEY,
|
||
|
|
algorithm='HS256'
|
||
|
|
)
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'code': 200,
|
||
|
|
'message': '登录成功',
|
||
|
|
'data': {
|
||
|
|
'token': token,
|
||
|
|
'user': {
|
||
|
|
'id': user['id'],
|
||
|
|
'username': user['username'],
|
||
|
|
'email': user['email'],
|
||
|
|
'role': user['role']
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
@app.route('/api/auth/userinfo', methods=['GET'])
|
||
|
|
@token_required
|
||
|
|
def get_userinfo():
|
||
|
|
user = next((u for u in USERS if u['id'] == request.user_id), None)
|
||
|
|
if not user:
|
||
|
|
return jsonify({'code': 404, 'message': '用户不存在'}), 404
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'code': 200,
|
||
|
|
'message': '获取成功',
|
||
|
|
'data': {
|
||
|
|
'id': user['id'],
|
||
|
|
'username': user['username'],
|
||
|
|
'email': user['email'],
|
||
|
|
'role': user['role']
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
app.run(debug=True, port=3000)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Java Spring Boot 示例
|
||
|
|
|
||
|
|
```java
|
||
|
|
@RestController
|
||
|
|
@RequestMapping("/api/auth")
|
||
|
|
@CrossOrigin(origins = "*")
|
||
|
|
public class AuthController {
|
||
|
|
|
||
|
|
@PostMapping("/login")
|
||
|
|
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
|
||
|
|
// 验证用户
|
||
|
|
User user = authenticateUser(request.getUsername(), request.getPassword());
|
||
|
|
|
||
|
|
if (user == null) {
|
||
|
|
return ResponseEntity.status(401).body(new ApiResponse(401, "用户名或密码错误"));
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成 JWT token
|
||
|
|
String token = JwtUtils.generateToken(user);
|
||
|
|
|
||
|
|
return ResponseEntity.ok(new ApiResponse(200, "登录成功",
|
||
|
|
new LoginResponse(token, user)));
|
||
|
|
}
|
||
|
|
|
||
|
|
@GetMapping("/userinfo")
|
||
|
|
@PreAuthorize("isAuthenticated()")
|
||
|
|
public ResponseEntity<?> getUserInfo() {
|
||
|
|
User user = getCurrentUser();
|
||
|
|
return ResponseEntity.ok(new ApiResponse(200, "获取成功", user));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 注意事项
|
||
|
|
|
||
|
|
1. **密码加密**:在生产环境中,必须加密存储密码(使用 bcrypt 等)
|
||
|
|
2. **HTTPS**:生产环境必须使用 HTTPS
|
||
|
|
3. **CORS**:根据前端域名配置 CORS
|
||
|
|
4. **Token 有效期**:建议设置合理的过期时间(如 7 天)
|
||
|
|
5. **错误处理**:返回统一的错误格式
|
||
|
|
6. **日志记录**:记录所有登录尝试
|
||
|
|
7. **速率限制**:防止暴力破解
|
||
|
|
|
||
|
|
## 与前端集成
|
||
|
|
|
||
|
|
1. 启动后端服务:`node server.js`
|
||
|
|
2. 启动前端开发服务:`npm run dev`
|
||
|
|
3. 前端会自动从 `http://localhost:3000/api` 调用后端接口
|
||
|
|
4. 在登录页输入 `admin / 123456` 进行测试
|