Files
smsweb/DEVELOPMENT.md
OpenClaw Agent 4e5e93660d Initial commit: SMS Receiver Web Service
Features:
- Receive SMS from TranspondSms Android APP
- HMAC-SHA256 signature verification (optional)
- SQLite database storage
- Web UI with login authentication
- Multiple API tokens support
- Timezone conversion (Asia/Shanghai)
- Search, filter, and statistics
- Auto refresh and session management

Tech Stack:
- Flask 3.0
- SQLite database
- HTML5/CSS3 responsive design
2026-02-06 23:23:49 +00:00

21 KiB
Raw Blame History

短信转发接收端 - 开发文档

目录


项目概述

技术架构

┌─────────────────┐         ┌─────────────────┐         ┌─────────────────┐
│  Android APP   │         │  Flask Web     │         │  SQLite DB      │
│ TranspondSms   │────────▶│  Server        │────────▶│  sms_receiver   │
│                 │  POST   │                 │  Store  │  .db            │
└─────────────────┘         └─────────────────┘         └─────────────────┘
                                   │
                                   ▼
                           ┌─────────────────┐
                           │  Web UI         │
                           │  (登录认证)     │
                           └─────────────────┘

核心功能

  1. 短信接收:接收 TranspondSms Android APP 转发的短信
  2. 签名验证HMAC-SHA256 签名验证,防止伪造请求
  3. 数据存储SQLite 数据库存储短信和日志
  4. Web 管理:登录验证 + 短信列表、详情、日志、统计
  5. 时区转换UTC 存储时区转换显示
  6. Token 配置:支持多设备、多 Token 配置

实现逻辑

1. 整体数据流

Android APP 接收短信
    │
    ├─ 解析短信内容支持多PDU
    ├─ 提取发送方、内容、时间戳
    ├─ 可选生成签名HMAC-SHA256
    │
    ▼
POST 请求发送到 /api/receive
    │
    ├─ 解析 multipart/form-data
    ├─ 验证必填参数from, content
    ├─ 可选:验证签名
    │   ├─ 检查时间戳是否过期
    │   ├─ 生成期望的签名
    │   └─ 比较签名是否匹配
    │
    ├─ 保存到数据库
    │   ├─ 存储为 UTC 时间
    │   ├─ 关联 Token 和 Secret
    │   └─ 记录接收日志
    │
    ▼
返回成功响应

2. 签名验证逻辑

TranspondSms 的签名规则:

# 1. 拼接待签名字符串
string_to_sign = timestamp + "\n" + secret

# 2. HMAC-SHA256 计算
hmac_code = hmac.new(
    secret.encode('utf-8'),
    string_to_sign.encode('utf-8'),
    digestmod=hashlib.sha256
).digest()

# 3. Base64 编码
sign_bytes = base64.b64encode(hmac_code)

# 4. URL 编码
sign = urllib.parse.quote(sign_bytes.decode())

防重放攻击

  • 检查时间戳是否在允许范围内默认1小时
  • 超出范围的请求拒绝

3. 时区转换逻辑

数据库存储UTC
    │
    ├─ created_at: 2024-02-06 14:30:00 (UTC)
    └─ timestamp: 1707223800000 (毫秒时间戳)
    │
    ▼
读取时转换
    │
    ├─ UTC 时间 + 时区偏移8小时
    └─ datetime.fromtimestamp(timestamp / 1000)
    │
    ▼
显示(本地时间)
    │
    └─ created_at: 2024-02-06 22:30:00 (Asia/Shanghai)

4. 登录验证流程

用户访问页面
    │
    ▼
检查 session['logged_in']
    │
    ├─ 已登录 ──▶ 更新 last_activity ──▶ 允许访问
    │
    └─ 未登录 ──▶ 跳转到 /login
                           │
                           ▼
                          提交表单
                           │
                           ├─ 验证用户名和密码
                           │
                           ├─ 成功:
                           │   ├─ session['logged_in'] = True
                           │   ├─ session['username'] = username
                           │   ├─ session['login_time'] = now
                           │   └─ 跳转到原页面或首页
                           │
                           └─ 失败:显示错误消息

会话超时检查

last_activity = session.get('last_activity')
if now - last_activity > SESSION_LIFETIME:
    # 清空会话,重定向到登录页
    session.clear()
    return redirect('/login')

5. Token 匹配逻辑

# 接收请求时
token = request.form.get('token')  # 从参数获取

# 在配置中查找对应的 secret
for token_config in API_TOKENS:
    if token_config['token'] == token and token_config['enabled']:
        secret = token_config['secret']
        break

# 使用找到的 secret 进行签名验证
if secret and SIGN_VERIFY:
    verify_sign(secret, sign, timestamp)

6. 数据库设计

sms_messages 表

字段 类型 说明
id INTEGER 主键,自增
from_number TEXT 发送方手机号
content TEXT 短信内容
timestamp INTEGER 原始时间戳(毫秒)
device_info TEXT 设备信息(可选)
sim_info TEXT SIM 卡信息(可选)
sign_verified INTEGER 是否通过签名验证0/1
ip_address TEXT 来源 IP 地址
created_at TIMESTAMP 创建时间UTC

receive_logs 表

字段 类型 说明
id INTEGER 主键,自增
from_number TEXT 发送方手机号
content TEXT 短信内容
timestamp INTEGER 时间戳
sign TEXT 签名
sign_valid INTEGER 签名是否有效0/1/null
ip_address TEXT IP 地址
status TEXT 处理状态success/error
error_message TEXT 错误消息
created_at TIMESTAMP 创建时间UTC**

分层架构

┌─────────────────────────────────────────┐
│         Flask Application Layer        │
│  (app.py - 路由、业务逻辑、会话管理)    │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│         Database Layer                 │
│  (database.py - 数据库操作、时区转换)   │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│         SQLite Database                 │
│  (sms_receiver.db - 数据持久化)         │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│         Template Layer                  │
│  (templates/ - HTML、CSS、JS)           │
└─────────────────────────────────────────┘

核心模块职责

模块 职责
app.py Flask 主应用,路由注册,业务逻辑
config.py 配置加载,从 config.json 读取配置
database.py 数据库模型CRUD 操作,时区转换
sign_verify.py 签名生成和验证
templates/ HTML 模板,前端展示

使用指南

前置要求

  • Python 3.7+
  • Flask 3.0+
  • SQLite3

安装依赖

pip install -r requirements.txt

配置文件

创建 config.json

{
  "server": {
    "host": "0.0.0.0",
    "port": 9518,
    "debug": false
  },
  "security": {
    "enabled": true,
    "username": "admin",
    "password": "YourStrongPassword123",
    "session_lifetime": 3600,
    "secret_key": "RandomSecretKeyHere",
    "sign_verify": true,
    "sign_max_age": 3600000
  },
  "sms": {
    "max_messages": 10000,
    "auto_cleanup": true,
    "cleanup_days": 30
  },
  "database": {
    "path": "sms_receiver.db"
  },
  "timezone": "Asia/Shanghai",
  "api_tokens": [
    {
      "name": "我的手机",
      "token": "my_phone_token",
      "secret": "my_phone_secret",
      "enabled": true
    }
  ]
}

启动服务

python3 app.py

服务启动后访问:http://你的IP:9518

配置 TranspondSms APP

  1. 下载并安装 TranspondSms APP
  2. 打开 APP进入"发送方"页面
  3. 添加"网页通知"
  4. 填写配置:
Token (URL): http://你的服务器IP:9518/api/receive?token=my_phone_token
Secret: my_phone_secret
  1. 点击"测试"按钮,验证是否成功
  2. 配置转发规则(如"转发全部"

使用 Web 界面

登录

查看短信

  • 短信列表:主页显示所有收到短信
  • 搜索:支持按号码或内容搜索
  • 筛选:按发送方号码快捷筛选
  • 详情:点击短信查看完整内容和元数据

查看日志

  • 访问"接收日志"页面
  • 查看每次请求的处理结果
  • 包括签名验证状态、IP 地址、错误信息

统计信息

  • 访问"统计信息"页面
  • 查看短信总数、今日、本周
  • 签名验证比例
  • 发送方号码排行榜
  • 清理旧数据

部署指南

开发环境部署

# 克隆项目
git clone <your-repo-url>
cd smsweb

# 创建虚拟环境(推荐)
python3 -m venv venv
source venv/bin/activate

# 安装依赖
pip install -r requirements.txt

# 配置 config.json
cp config.example.json config.json
vim config.json

# 启动服务
python3 app.py

生产环境部署(推荐方案)

1. 使用 Gunicorn + Nginx

安装 Gunicorn

pip install gunicorn

创建 systemd 服务

sudo vim /etc/systemd/system/sms-receiver.service

内容:

[Unit]
Description=SMS Receiver Service
After=network.target

[Service]
Type=notify
User=www-data
WorkingDirectory=/var/www/sms-receiver
Environment="PATH=/var/www/sms-receiver/venv/bin"
ExecStart=/var/www/sms-receiver/venv/bin/gunicorn \
    -w 4 -b 127.0.0.1:9518 \
    --timeout 120 \
    --access-logfile /var/log/sms-receiver/access.log \
    --error-logfile /var/log/sms-receiver/error.log \
    app:app
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

启动服务

sudo systemctl daemon-reload
sudo systemctl enable sms-receiver
sudo systemctl start sms-receiver
sudo systemctl status sms-receiver

配置 Nginx 反向代理

sudo vim /etc/nginx/sites-available/sms-receiver

内容:

upstream sms_receiver {
    server 127.0.0.1:9518;
}

server {
    listen 80;
    server_name sms.example.com;

    access_log /var/log/nginx/sms-receiver-access.log;
    error_log /var/log/nginx/sms-receiver-error.log;

    location / {
        proxy_pass http://sms_receiver;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
        proxy_buffering off;
    }

    # 接收 API 不需要登录,但对客户端透明
    location /api/receive {
        proxy_pass http://sms_receiver/api/receive;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

启用站点

sudo ln -s /etc/nginx/sites-available/sms-receiver /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

2. 配置 HTTPSLet's Encrypt

sudo certbot --nginx -d sms.example.com

3. 防火墙配置

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

4. 日志轮转

sudo vim /etc/logrotate.d/sms-receiver

内容:

/var/log/sms-receiver/*.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        systemctl reload sms-receiver >/dev/null 2>&1 || true
    endscript
}

Docker 部署

创建 Dockerfile

FROM python:3.11-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 9518

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:9518", "app:app"]

创建 docker-compose.yml

version: '3.8'

services:
  sms-receiver:
    build: .
    ports:
      - "9518:9518"
    volumes:
      - ./sms_receiver.db:/app/sms_receiver.db
      - ./config.json:/app/config.json
      - ./logs:/app/logs
    environment:
      - FLASK_ENV=production
    restart: unless-stopped

启动

docker-compose up -d

安全加固

  1. 修改默认密码:首次部署后立即修改登录密码
  2. 使用强密码至少16位包含大小写字母、数字、特殊字符
  3. 启用 HTTPS:使用 Let's Encrypt 免费证书
  4. 限制访问:配置防火墙,只开放必要端口
  5. 启用签名验证:设置 Token 的 secret
  6. 定期更新:定期更新 Python 和 Flask 版本

监控和维护

查看日志

# 应用日志
tail -f /var/log/sms-receiver/error.log

# Nginx 日志
tail -f /var/log/nginx/sms-receiver-error.log

备份数据库

#!/bin/bash
BACKUP_DIR="/backup/sms-receiver"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

# 备份数据库
cp /var/www/sms-receiver/sms_receiver.db $BACKUP_DIR/sms_receiver_$DATE.db

# 备份配置
cp /var/www/sms-receiver/config.json $BACKUP_DIR/config_$DATE.json

# 删除30天前的备份
find $BACKUP_DIR -name "*.db" -mtime +30 -delete
find $BACKUP_DIR -name "*.json" -mtime +30 -delete

API 文档

POST /api/receive

接收短信接口。

请求方式POST multipart/form-data

URL 参数

  • token (可选): API Token用于匹配对应的 secret

表单参数

参数 类型 必填 说明
from string 发送方手机号
content string 短信内容
timestamp string 时间戳(毫秒),用于签名验证
sign string 签名HMAC-SHA256 + Base64 + URL Encode
device string 设备信息
sim string SIM 卡信息

请求示例

curl -X POST http://your-server:9518/api/receive?token=my_token \
  -F "from=10086" \
  -F "content=验证码: 123456" \
  -F "timestamp=1707223800000" \
  -F "sign=xxx"

响应示例

成功:

{
  "success": true,
  "message_id": 123,
  "message": "短信已接收"
}

失败:

{
  "error": "缺少必填参数"
}

HTTP 状态码

  • 200: 成功
  • 400: 参数错误
  • 403: 签名验证失败
  • 500: 服务器错误

GET /api/messages

获取短信列表(需要登录)。

URL 参数

参数 类型 必填 说明
page int 页码默认1
limit int 每页数量默认20
from string 按发送方号码筛选
search string 搜索内容或号码

响应示例

{
  "success": true,
  "data": [
    {
      "id": 1,
      "from_number": "10086",
      "content": "验证码: 123456",
      "timestamp": 1707223800000,
      "local_timestamp": "2024-02-06 22:30:00",
      "created_at": "2024-02-06 14:30:00",
      "sign_verified": true
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20
}

GET /api/statistics

获取统计信息(需要登录)。

响应示例

{
  "success": true,
  "data": {
    "total": 100,
    "today": 10,
    "week": 50,
    "verified": 80,
    "unverified": 20
  }
}

配置说明

完整配置示例

{
  "app": {
    "name": "短信转发接收端",
    "version": "1.0.0"
  },
  "server": {
    "host": "0.0.0.0",
    "port": 9518,
    "debug": false
  },
  "security": {
    "enabled": true,
    "username": "admin",
    "password": "YourStrongPassword123",
    "session_lifetime": 3600,
    "secret_key": "RandomSecretKeyChangeMe",
    "sign_verify": true,
    "sign_max_age": 3600000
  },
  "sms": {
    "max_messages": 10000,
    "auto_cleanup": true,
    "cleanup_days": 30
  },
  "database": {
    "path": "sms_receiver.db"
  },
  "timezone": "Asia/Shanghai",
  "api_tokens": [
    {
      "name": "主手机",
      "token": "main_phone_token",
      "secret": "main_phone_secret_key",
      "enabled": true
    },
    {
      "name": "备用机",
      "token": "backup_phone_token",
      "secret": "backup_phone_secret_key",
      "enabled": true
    },
    {
      "name": "测试设备",
      "token": "test_token",
      "secret": "",
      "enabled": false
    }
  ]
}

配置项详解

server - 服务器配置

字段 类型 默认值 说明
host string 0.0.0.0 监听地址
port int 9518 监听端口
debug bool false 调试模式

security - 安全配置

字段 类型 默认值 说明
enabled bool true 是否启用登录验证
username string admin 登录用户名
password string admin123 登录密码
session_lifetime int 3600 会话有效期(秒)
secret_key string - Flask 会话密钥
sign_verify bool true 是否验证签名
sign_max_age int 3600000 签名最大有效时间(毫秒)

sms - 短信配置

字段 类型 默认值 说明
max_messages int 10000 最多保留短信数
auto_cleanup bool true 是否自动清理老数据
cleanup_days int 30 清理多少天前的数据

api_tokens - API Token 配置

字段 类型 说明
name string 配置名称(可选)
token string Token 值,用于匹配 secret
secret string 密钥,用于签名验证(可选)
enabled bool 是否启用此 Token

常见问题

Q1: 如何禁用登录验证?

config.json 中设置:

{
  "security": {
    "enabled": false
  }
}

Q2:签名验证失败怎么办?

检查以下几点:

  1. 时间戳是否正确确保客户端时间准确误差不超过1小时
  2. Secret 是否匹配:确保客户端和服务器端的 secret 完全一致
  3. 签名生成算法使用正确的算法HMAC-SHA256

调试签名

# 生成签名
import time, hmac, hashlib, base64, urllib.parse

timestamp = str(int(time.time() * 1000))
secret = "your_secret"
string_to_sign = f"{timestamp}\n{secret}"
sign = urllib.parse.quote(base64.b64encode(
    hmac.new(secret.encode(), string_to_sign.encode(), hashlib.sha256).digest()
).decode())

print(f"Timestamp: {timestamp}")
print(f"Sign: {sign}")

Q3: 如何配置多个设备?

api_tokens 中添加多个配置:

{
  "api_tokens": [
    {
      "name": "设备A",
      "token": "device_a",
      "secret": "secret_a",
      "enabled": true
    },
    {
      "name": "设备B",
      "token": "device_b",
      "secret": "secret_b",
      "enabled": true
    }
  ]
}

在 APP 中配置不同的设备使用不同的 Token。

Q4: 会话总是过期?

调整 session_lifetime

{
  "security": {
    "session_lifetime": 86400  // 24小时
  }
}

Q5: 如何备份数据?

直接复制数据库文件:

cp sms_receiver.db sms_receiver.db.backup

或者使用 SQLite 导出:

sqlite3 sms_receiver.db ".dump" > backup.sql

Q6: 如何清理所有数据?

删除数据库文件,重启服务会自动重建:

rm sms_receiver.db
python3 app.py

Q7: 时间显示不正确?

检查时区配置:

{
  "timezone": "Asia/Shanghai"
}

可用时区列表:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones


开发规范

代码风格

  • 遵循 PEP 8 Python 代码规范
  • 使用有意义的变量和函数名
  • 添加必要的类型注解

Git 提交规范

feat: 添加新功能
fix: 修复 bug
docs: 更新文档
style: 代码格式化
refactor: 重构
test: 添加测试
chore: 构建/工具链

测试建议

# 测试签名生成
python3 sign_verify.py

# 测试 API
curl -X POST http://localhost:9518/api/receive \
  -F "from=10086" \
  -F "content=test"

许可证

MIT License

联系方式