本文将带你从零开始构建一个完整的局域网邮件系统,包含用户管理、邮件收发、JWT认证等核心功能。适用于企业内部、学校或家庭局域网环境。
📋 目录
系统概述
功能特性
本邮件系统实现了以下核心功能:
✅ 用户注册与登录
✅ JWT身份认证
✅ 发送邮件
✅ 接收邮件
✅ 收件箱/发件箱管理
✅ 邮件已读/未读状态
✅ 响应式UI界面
技术选型
| 技术 | 版本 | 用途 |
|---|---|---|
| Python | 3.8+ | 后端语言 |
| Flask | 3.0.0 | Web框架 |
| SQLite | 3.x | 数据库 |
| PyJWT | 2.8.0 | JWT认证 |
| HTML5/CSS3/JS | - | 前端界面 |
技术架构
整体架构图
┌─────────────────────────────────────┐
│ 客户端层 (Browser) │
│ HTML5 + CSS3 + JavaScript │
└──────────────┬──────────────────────┘
│ HTTP/HTTPS
│ RESTful API
┌──────────────▼──────────────────────┐
│ 应用层 (Flask) │
│ - 路由处理 │
│ - JWT认证 │
│ - 业务逻辑 │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ 数据层 (SQLite) │
│ - users 表 │
│ - emails 表 │
└─────────────────────────────────────┘
工作流程
1. 用户认证流程
用户登录 → 验证密码 → 生成JWT Token
→ 返回Token → 客户端存储 → 后续请求携带Token
2. 邮件发送流程
填写邮件 → 发送POST请求 → 验证Token
→ 插入数据库 → 返回成功响应
数据库设计
ER图
┌─────────────────┐ ┌─────────────────┐
│ users │ │ emails │
├─────────────────┤ ├─────────────────┤
│ id (PK) │◄───────┐│ id (PK) │
│ username │ ││ sender_id (FK) │
│ password │ │└─receiver_id (FK)│
│ email │ │ subject │
│ created_at │ │ content │
└─────────────────┘ │ is_read │
│ created_at │
└─────────────────┘
SQL建表语句
sql
-- 用户表
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 邮件表
CREATE TABLE emails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender_id INTEGER NOT NULL,
receiver_id INTEGER NOT NULL,
subject TEXT NOT NULL,
content TEXT NOT NULL,
is_read INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (sender_id) REFERENCES users(id),
FOREIGN KEY (receiver_id) REFERENCES users(id)
);
后端实现
1. 项目结构
mail_system/
├── server.py # Flask后端
├── requirements.txt # 依赖包
├── mail_system.db # 数据库(自动生成)
└── static/
└── index.html # 前端页面
2. 依赖配置 (requirements.txt)
txt
Flask==3.0.0
flask-cors==4.0.0
PyJWT==2.8.0
3. 完整后端代码 (server.py)
python
"""
本地局域网邮件管理系统 - 后端服务器
"""
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
import sqlite3
import hashlib
import jwt
import datetime
from functools import wraps
app = Flask(__name__)
CORS(app)
# 配置
app.config['SECRET_KEY'] = 'your-secret-key-change-in-production'
app.config['DATABASE'] = 'mail_system.db'
# ============ 数据库操作 ============
def init_db():
"""初始化数据库"""
conn = sqlite3.connect(app.config['DATABASE'])
cursor = conn.cursor()
# 创建用户表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 创建邮件表
cursor.execute('''
CREATE TABLE IF NOT EXISTS emails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender_id INTEGER NOT NULL,
receiver_id INTEGER NOT NULL,
subject TEXT NOT NULL,
content TEXT NOT NULL,
is_read INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (sender_id) REFERENCES users(id),
FOREIGN KEY (receiver_id) REFERENCES users(id)
)
''')
conn.commit()
conn.close()
def get_db():
"""获取数据库连接"""
conn = sqlite3.connect(app.config['DATABASE'])
conn.row_factory = sqlite3.Row
return conn
# ============ 工具函数 ============
def hash_password(password):
"""密码哈希加密"""
return hashlib.sha256(password.encode()).hexdigest()
def token_required(f):
"""JWT认证装饰器"""
@wraps(f)
def decorated(current_user_id, *args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': '缺少token'}), 401
try:
token = token.split(' ')[1]
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
current_user_id = data['user_id']
except:
return jsonify({'message': 'token无效'}), 401
return f(current_user_id, *args, **kwargs)
return decorated
# ============ API路由 ============
@app.route('/')
def index():
"""首页"""
return send_from_directory('static', 'index.html')
@app.route('/api/register', methods=['POST'])
def register():
"""用户注册"""
data = request.get_json()
username = data.get('username')
password = data.get('password')
email = data.get('email')
if not all([username, password, email]):
return jsonify({'message': '缺少必要参数'}), 400
conn = get_db()
cursor = conn.cursor()
try:
hashed_password = hash_password(password)
cursor.execute(
'INSERT INTO users (username, password, email) VALUES (?, ?, ?)',
(username, hashed_password, email)
)
conn.commit()
return jsonify({'message': '注册成功'}), 201
except sqlite3.IntegrityError:
return jsonify({'message': '用户名或邮箱已存在'}), 409
finally:
conn.close()
@app.route('/api/login', methods=['POST'])
def login():
"""用户登录"""
data = request.get_json()
username = data.get('username')
password = data.get('password')
if not all([username, password]):
return jsonify({'message': '缺少用户名或密码'}), 400
conn = get_db()
cursor = conn.cursor()
hashed_password = hash_password(password)
cursor.execute(
'SELECT id, username, email FROM users WHERE username = ? AND password = ?',
(username, hashed_password)
)
user = cursor.fetchone()
conn.close()
if user:
# 生成JWT token
token = jwt.encode({
'user_id': user['id'],
'username': user['username'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24)
}, app.config['SECRET_KEY'], algorithm='HS256')
return jsonify({
'message': '登录成功',
'token': token,
'user': {
'id': user['id'],
'username': user['username'],
'email': user['email']
}
}), 200
else:
return jsonify({'message': '用户名或密码错误'}), 401
@app.route('/api/users', methods=['GET'])
@token_required
def get_users(current_user_id):
"""获取用户列表"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT id, username, email FROM users WHERE id != ?', (current_user_id,))
users = cursor.fetchall()
conn.close()
users_list = [dict(user) for user in users]
return jsonify({'users': users_list}), 200
@app.route('/api/emails/send', methods=['POST'])
@token_required
def send_email(current_user_id):
"""发送邮件"""
data = request.get_json()
receiver_id = data.get('receiver_id')
subject = data.get('subject')
content = data.get('content')
if not all([receiver_id, subject, content]):
return jsonify({'message': '缺少必要参数'}), 400
conn = get_db()
cursor = conn.cursor()
try:
cursor.execute(
'INSERT INTO emails (sender_id, receiver_id, subject, content) VALUES (?, ?, ?, ?)',
(current_user_id, receiver_id, subject, content)
)
conn.commit()
return jsonify({'message': '邮件发送成功'}), 201
except Exception as e:
return jsonify({'message': f'发送失败: {str(e)}'}), 500
finally:
conn.close()
@app.route('/api/emails/inbox', methods=['GET'])
@token_required
def get_inbox(current_user_id):
"""获取收件箱"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
SELECT e.id, e.subject, e.content, e.is_read, e.created_at,
u.username as sender_name, u.email as sender_email
FROM emails e
JOIN users u ON e.sender_id = u.id
WHERE e.receiver_id = ?
ORDER BY e.created_at DESC
''', (current_user_id,))
emails = cursor.fetchall()
conn.close()
emails_list = [dict(email) for email in emails]
return jsonify({'emails': emails_list}), 200
@app.route('/api/emails/sent', methods=['GET'])
@token_required
def get_sent(current_user_id):
"""获取发件箱"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
SELECT e.id, e.subject, e.content, e.is_read, e.created_at,
u.username as receiver_name, u.email as receiver_email
FROM emails e
JOIN users u ON e.receiver_id = u.id
WHERE e.sender_id = ?
ORDER BY e.created_at DESC
''', (current_user_id,))
emails = cursor.fetchall()
conn.close()
emails_list = [dict(email) for email in emails]
return jsonify({'emails': emails_list}), 200
@app.route('/api/emails/<int:email_id>/read', methods=['PUT'])
@token_required
def mark_as_read(current_user_id, email_id):
"""标记为已读"""
conn = get_db()
cursor = conn.cursor()
cursor.execute(
'UPDATE emails SET is_read = 1 WHERE id = ? AND receiver_id = ?',
(email_id, current_user_id)
)
conn.commit()
conn.close()
return jsonify({'message': '已标记为已读'}), 200
@app.route('/api/emails/<int:email_id>', methods=['DELETE'])
def delete_email(current_user_id, email_id):
"""删除邮件"""
conn = get_db()
cursor = conn.cursor()
cursor.execute(
'DELETE FROM emails WHERE id = ? AND (sender_id = ? OR receiver_id = ?)',
(email_id, current_user_id, current_user_id)
)
conn.commit()
conn.close()
return jsonify({'message': '删除成功'}), 200
# ============ 启动服务器 ============
if __name__ == '__main__':
init_db()
print("=" * 50)
print("本地局域网邮件系统启动成功!")
print("访问地址: http://localhost:5000")
print("=" * 50)
app.run(host='0.0.0.0', port=5000, debug=True)
前端实现
完整前端代码 (static/index.html)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局域网邮件系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 90%;
max-width: 1200px;
background: white;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
/* 登录注册页面 */
.auth-container {
max-width: 400px;
padding: 40px;
}
.auth-container h2 {
text-align: center;
margin-bottom: 30px;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
}
.btn {
width: 100%;
padding: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.btn:hover {
transform: translateY(-2px);
}
.switch-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.switch-link a {
color: #667eea;
text-decoration: none;
font-weight: bold;
}
/* 邮件系统主界面 */
.mail-app {
display: none;
height: 600px;
}
.mail-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logout-btn {
padding: 8px 20px;
background: rgba(255,255,255,0.2);
border: 2px solid white;
color: white;
border-radius: 5px;
cursor: pointer;
}
.mail-body {
display: flex;
height: calc(100% - 80px);
}
.sidebar {
width: 200px;
background: #f5f5f5;
padding: 20px;
border-right: 1px solid #ddd;
}
.menu-item {
padding: 12px;
margin-bottom: 10px;
background: white;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.menu-item:hover {
background: #667eea;
color: white;
}
.menu-item.active {
background: #667eea;
color: white;
}
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.compose-btn {
width: 100%;
padding: 12px;
background: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-bottom: 20px;
}
/* 邮件列表 */
.email-list {
display: none;
}
.email-list.active {
display: block;
}
.email-item {
padding: 15px;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.email-item:hover {
background: #f9f9f9;
}
.email-item.unread {
background: #e3f2fd;
font-weight: bold;
}
.email-subject {
font-size: 16px;
margin-bottom: 5px;
color: #333;
}
.email-meta {
font-size: 12px;
color: #666;
}
/* 写邮件表单 */
.compose-form {
display: none;
}
.compose-form.active {
display: block;
}
.compose-form select,
.compose-form input,
.compose-form textarea {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 2px solid #ddd;
border-radius: 5px;
}
.compose-form textarea {
min-height: 200px;
resize: vertical;
}
.alert {
padding: 12px;
margin-bottom: 15px;
border-radius: 5px;
display: none;
}
.alert.success {
background: #d4edda;
color: #155724;
}
.alert.error {
background: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<div class="container">
<!-- 登录页面 -->
<div id="loginPage" class="auth-container">
<h2>登录邮件系统</h2>
<div id="loginAlert" class="alert"></div>
<form id="loginForm">
<div class="form-group">
<label>用户名</label>
<input type="text" id="loginUsername" required>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" id="loginPassword" required>
</div>
<button type="submit" class="btn">登录</button>
</form>
<div class="switch-link">
还没有账号? <a href="#" onclick="showRegister()">立即注册</a>
</div>
</div>
<!-- 注册页面 -->
<div id="registerPage" class="auth-container" style="display: none;">
<h2>注册新账号</h2>
<div id="registerAlert" class="alert"></div>
<form id="registerForm">
<div class="form-group">
<label>用户名</label>
<input type="text" id="registerUsername" required>
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" id="registerEmail" required>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" id="registerPassword" required>
</div>
<button type="submit" class="btn">注册</button>
</form>
<div class="switch-link">
已有账号? <a href="#" onclick="showLogin()">立即登录</a>
</div>
</div>
<!-- 邮件系统主界面 -->
<div id="mailApp" class="mail-app">
<div class="mail-header">
<h1>📧 局域网邮件系统</h1>
<div class="user-info">
<span id="currentUser"></span>
<button class="logout-btn" onclick="logout()">退出</button>
</div>
</div>
<div class="mail-body">
<div class="sidebar">
<button class="compose-btn" onclick="showCompose()">✏️ 写邮件</button>
<div class="menu-item active" onclick="showInbox()">📥 收件箱</div>
<div class="menu-item" onclick="showSent()">📤 发件箱</div>
</div>
<div class="content">
<div id="inboxList" class="email-list active"></div>
<div id="sentList" class="email-list"></div>
<div id="composeForm" class="compose-form">
<h3>写邮件</h3>
<div id="composeAlert" class="alert"></div>
<form id="sendEmailForm">
<select id="receiverId" required>
<option value="">选择收件人</option>
</select>
<input type="text" id="emailSubject" placeholder="主题" required>
<textarea id="emailContent" placeholder="邮件内容" required></textarea>
<button type="submit" class="btn">发送</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
const API_URL = 'http://localhost:5000/api';
let currentToken = null;
let currentUser = null;
function showLogin() {
document.getElementById('loginPage').style.display = 'block';
document.getElementById('registerPage').style.display = 'none';
}
function showRegister() {
document.getElementById('loginPage').style.display = 'none';
document.getElementById('registerPage').style.display = 'block';
}
function showAlert(elementId, message, type) {
const alert = document.getElementById(elementId);
alert.textContent = message;
alert.className = `alert ${type}`;
alert.style.display = 'block';
setTimeout(() => alert.style.display = 'none', 3000);
}
// 注册
document.getElementById('registerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('registerUsername').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
try {
const response = await fetch(`${API_URL}/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, email, password })
});
const data = await response.json();
if (response.ok) {
showAlert('registerAlert', '注册成功!', 'success');
setTimeout(showLogin, 1500);
} else {
showAlert('registerAlert', data.message, 'error');
}
} catch (error) {
showAlert('registerAlert', '网络错误', 'error');
}
});
// 登录
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
try {
const response = await fetch(`${API_URL}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (response.ok) {
currentToken = data.token;
currentUser = data.user;
showMailApp();
} else {
showAlert('loginAlert', data.message, 'error');
}
} catch (error) {
showAlert('loginAlert', '网络错误', 'error');
}
});
function showMailApp() {
document.getElementById('loginPage').style.display = 'none';
document.getElementById('mailApp').style.display = 'block';
document.getElementById('currentUser').textContent = currentUser.username;
loadUsers();
showInbox();
}
async function loadUsers() {
const response = await fetch(`${API_URL}/users`, {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
const data = await response.json();
const select = document.getElementById('receiverId');
select.innerHTML = '<option value="">选择收件人</option>';
data.users.forEach(user => {
const option = document.createElement('option');
option.value = user.id;
option.textContent = `${user.username} (${user.email})`;
select.appendChild(option);
});
}
async function showInbox() {
hideAll();
document.getElementById('inboxList').classList.add('active');
const response = await fetch(`${API_URL}/emails/inbox`, {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
const data = await response.json();
displayEmails(data.emails, 'inbox');
}
async function showSent() {
hideAll();
document.getElementById('sentList').classList.add('active');
const response = await fetch(`${API_URL}/emails/sent`, {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
const data = await response.json();
displayEmails(data.emails, 'sent');
}
function displayEmails(emails, type) {
const listId = type === 'inbox' ? 'inboxList' : 'sentList';
const list = document.getElementById(listId);
list.innerHTML = '';
if (emails.length === 0) {
list.innerHTML = '<p style="text-align:center;color:#999;">暂无邮件</p>';
return;
}
emails.forEach(email => {
const div = document.createElement('div');
div.className = `email-item ${!email.is_read && type === 'inbox' ? 'unread' : ''}`;
const name = type === 'inbox' ? email.sender_name : email.receiver_name;
div.innerHTML = `
<div class="email-subject">${email.subject}</div>
<div class="email-meta">
${type === 'inbox' ? '来自' : '发送至'}: ${name} |
${new Date(email.created_at).toLocaleString('zh-CN')}
</div>
`;
list.appendChild(div);
});
}
function showCompose() {
hideAll();
document.getElementById('composeForm').classList.add('active');
}
document.getElementById('sendEmailForm').addEventListener('submit', async (e) => {
e.preventDefault();
const receiver_id = document.getElementById('receiverId').value;
const subject = document.getElementById('emailSubject').value;
const content = document.getElementById('emailContent').value;
const response = await fetch(`${API_URL}/emails/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${currentToken}`
},
body: JSON.stringify({ receiver_id, subject, content })
});
const data = await response.json();
if (response.ok) {
showAlert('composeAlert', '发送成功!', 'success');
setTimeout(showSent, 1500);
} else {
showAlert('composeAlert', data.message, 'error');
}
});
function hideAll() {
document.querySelectorAll('.email-list, .compose-form').forEach(el => {
el.classList.remove('active');
});
}
function logout() {
currentToken = null;
currentUser = null;
document.getElementById('mailApp').style.display = 'none';
showLogin();
}
</script>
</body>
</html>
部署指南
环境准备
bash
# 检查Python版本(需要3.8+)
python --version
# 检查pip
pip --version
安装步骤
1. 创建项目目录
bash
mkdir mail_system
cd mail_system
mkdir static
2. 创建文件
将上面的代码分别保存为:
server.py(后端代码)static/index.html(前端代码)requirements.txt(依赖配置)
3. 安装依赖
bash
pip install -r requirements.txt
# 如果速度慢,使用国内镜像
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
4. 启动服务器
bash
python server.py
看到以下输出表示成功:
==================================================
本地局域网邮件系统启动成功!
访问地址: http://localhost:5000
==================================================
5. 访问系统
本机访问:
http://localhost:5000
局域网其他设备访问:
http://[服务器IP]:5000
查看服务器IP
Windows:
bash
ipconfig
# 查找 IPv4 地址
Linux/Mac:
bash
ifconfig
# 或
ip addr show
功能演示
1. 用户注册

- 打开浏览器访问
http://localhost:5000 - 点击"立即注册"
- 填写用户名、邮箱、密码
- 注册成功后自动跳转登录页
2. 用户登录
- 输入用户名和密码
- 点击"登录"按钮
- 登录成功进入邮件系统
3. 发送邮件
- 点击侧边栏"✏️ 写邮件"
- 从下拉列表选择收件人
- 输入邮件主题
- 输入邮件内容
- 点击"发送"
4. 查看邮件
收件箱:
- 显示接收到的所有邮件
- 未读邮件以蓝色背景高亮
- 点击邮件查看详情
发件箱:
- 显示已发送的所有邮件
- 查看发送历史
安全优化
1. 密码安全
当前实现(SHA-256)
python
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
推荐实现(bcrypt加盐)
python
import bcrypt
def hash_password(password):
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode(), salt)
def verify_password(password, hashed):
return bcrypt.checkpw(password.encode(), hashed)
2. JWT安全
python
# 设置合理的过期时间
token = jwt.encode({
'user_id': user_id,
'exp': datetime.utcnow() + timedelta(hours=24)
}, SECRET_KEY)
# 生产环境使用环境变量
import os
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'fallback-key')
3. SQL注入防护
python
# ✅ 正确: 使用参数化查询
cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
# ❌ 错误: 字符串拼接
cursor.execute(f'SELECT * FROM users WHERE id = {user_id}')
4. HTTPS配置
生产环境建议使用HTTPS:
python
# 使用Flask-Talisman
from flask_talisman import Talisman
Talisman(app, force_https=True)
5. 防火墙配置
Windows:
控制面板 → Windows Defender防火墙 → 高级设置
→ 入站规则 → 新建规则 → 端口 → TCP 5000
Linux (ufw):
bash
sudo ufw allow 5000/tcp
sudo ufw enable
功能扩展
1. 邮件附件
python
# 添加附件表
CREATE TABLE attachments (
id INTEGER PRIMARY KEY,
email_id INTEGER,
filename TEXT,
file_data BLOB,
FOREIGN KEY (email_id) REFERENCES emails(id)
);
2. 邮件搜索
python
@app.route('/api/emails/search', methods=['GET'])
@token_required
def search_emails(current_user_id):
keyword = request.args.get('q')
cursor.execute('''
SELECT * FROM emails
WHERE (sender_id = ? OR receiver_id = ?)
AND (subject LIKE ? OR content LIKE ?)
''', (current_user_id, current_user_id, f'%{keyword}%', f'%{keyword}%'))
3. 实时通知(WebSocket)
python
from flask_socketio import SocketIO, emit
socketio = SocketIO(app)
@socketio.on('connect')
def handle_connect():
emit('notification', {'message': '连接成功'})
4. 富文本编辑器
html
<!-- 集成Quill编辑器 -->
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<div id="editor"></div>
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
<script>
const quill = new Quill('#editor', {
theme: 'snow'
});
</script>
5. 数据库索引优化
sql
-- 提高查询性能
CREATE INDEX idx_sender ON emails(sender_id);
CREATE INDEX idx_receiver ON emails(receiver_id);
CREATE INDEX idx_created ON emails(created_at);
常见问题
Q1: 端口被占用怎么办?
A: 修改server.py中的端口号
python
app.run(host='0.0.0.0', port=8080, debug=True)
Q2: 无法从其他设备访问?
A: 检查以下几点:
- 防火墙是否开放端口
- 服务器IP地址是否正确
- 使用
0.0.0.0而不是127.0.0.1
Q3: Token过期怎么办?
A: 重新登录获取新Token,或实现Token刷新机制
Q4: 数据库锁定错误?
A: 使用连接池或增加超时时间
python
conn = sqlite3.connect(DATABASE, timeout=10)
Q5: 中文乱码?
A: 确保文件编码为UTF-8
python
# 在文件开头添加
# -*- coding: utf-8 -*-
性能优化
1. 分页加载
python
@app.route('/api/emails/inbox', methods=['GET'])
@token_required
def get_inbox(current_user_id):
page = request.args.get('page', 1, type=int)
per_page = 20
offset = (page - 1) * per_page
cursor.execute('''
SELECT * FROM emails
WHERE receiver_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
''', (current_user_id, per_page, offset))
2. 缓存优化
python
from functools import lru_cache
@lru_cache(maxsize=128)
def get_user_info(user_id):
# 缓存用户信息
pass
3. 数据库连接池
python
from DBUtils.PooledDB import PooledDB
pool = PooledDB(
creator=sqlite3,
maxconnections=6,
database=DATABASE
)
生产部署
使用Gunicorn
bash
# 安装Gunicorn
pip install gunicorn
# 启动(4个工作进程)
gunicorn -w 4 -b 0.0.0.0:5000 server:app
Docker容器化
dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "server.py"]
bash
# 构建镜像
docker build -t mail-system .
# 运行容器
docker run -p 5000:5000 mail-system
Nginx反向代理
nginx
server {
listen 80;
server_name mail.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
总结
本文完整实现了一个本地局域网邮件管理系统,包括:
✅ 系统架构 - 前后端分离,RESTful API设计
✅ 数据库设计 - 合理的表结构和关系
✅ 安全机制 - JWT认证、密码加密、SQL注入防护
✅ 完整代码 - 可直接运行的Flask后端和HTML前端
✅ 部署指南 - 详细的安装配置步骤
✅ 扩展方案 - 附件、搜索、性能优化等
适用场景
- 🏢 企业内部邮件系统
- 🏫 学校班级通知系统
- 🏠 家庭局域网消息中心
- 📚 Web开发学习项目
技术要点
- Flask框架 - 轻量级Web开发
- SQLite数据库 - 零配置、易部署
- JWT认证 - 无状态身份验证
- RESTful API - 标准化接口设计
- 响应式设计 - 适配各种设备