本地局域网邮件管理系统:从原理到实现的完整指南

本文将带你从零开始构建一个完整的局域网邮件系统,包含用户管理、邮件收发、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. 用户注册

  1. 打开浏览器访问 http://localhost:5000
  2. 点击"立即注册"
  3. 填写用户名、邮箱、密码
  4. 注册成功后自动跳转登录页

2. 用户登录

  1. 输入用户名和密码
  2. 点击"登录"按钮
  3. 登录成功进入邮件系统

3. 发送邮件

  1. 点击侧边栏"✏️ 写邮件"
  2. 从下拉列表选择收件人
  3. 输入邮件主题
  4. 输入邮件内容
  5. 点击"发送"

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: 检查以下几点:

  1. 防火墙是否开放端口
  2. 服务器IP地址是否正确
  3. 使用 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开发学习项目

技术要点

  1. Flask框架 - 轻量级Web开发
  2. SQLite数据库 - 零配置、易部署
  3. JWT认证 - 无状态身份验证
  4. RESTful API - 标准化接口设计
  5. 响应式设计 - 适配各种设备

参考资料

相关推荐
Tony Bai4 小时前
【Go 网络编程全解】12 本地高速公路:Unix 域套接字与网络设备信息
开发语言·网络·后端·golang·unix
oioihoii4 小时前
深入理解 C++ 现代类型推导:从 auto 到 decltype 与完美转发
java·开发语言·c++
报错小能手4 小时前
项目——基于C/S架构的预约系统平台 (1)
开发语言·c++·笔记·学习·架构
MYX_3095 小时前
第四章 多层感知机
开发语言·python
彬彬醤5 小时前
如何正确选择住宅IP?解析适配跨境、流媒体的网络工具
服务器·开发语言·网络·网络协议·tcp/ip
Yeats_Liao5 小时前
Go Web 编程快速入门 06 - 响应 ResponseWriter:状态码与头部
开发语言·后端·golang
chao1898445 小时前
C#模拟鼠标键盘操作的多种实现方案
开发语言·c#·计算机外设
mit6.8245 小时前
[Agent可视化] 编排工作流(Go) | Temporal引擎 | DAG调度器 | ReAct模式实现
开发语言·后端·golang
Devil枫5 小时前
HarmonyOS鸿蒙应用:仓颉语言与JavaScript核心差异深度解析
开发语言·javascript·ecmascript