ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SmsForwarder + Flask 搭建短信验证码自动接收服务,5步搞定自动化

SmsForwarder + Flask 搭建短信验证码自动接收服务,5步搞定自动化 你有没有过这种经历手机锁在抽屉里或者人不在工位短信验证码偏偏在这时候到了错过一次验证整个流程又得从头再来。更麻烦的是如果验证码不是一条两条而是要对接进自己写的脚本、管理系统或者自动化流程里靠人肉复制根本没法谈自动化。SmsForwarder是我用了好几年的安卓开源短信转发工具Flask则是Python里把接口快速立起来最顺手的选择这俩组合在一起就能做一个只需要5步的验证码自动接收服务手机收到短信SmsForwarder把它 POST 到一个自己写的 Flask APIAPI 负责解析验证码、入库、再对外开放一个查询接口。今天这篇就直接把整套搭建过程、代码、部署和踩坑记录都放出来适合自己写小工具的开发者也适合想让验证码自动流转的运维和自动化爱好者。1. 项目拆解SmsForwarder Flask 这套方案的思路1.1 为什么选这套组合短信自动化这件事方案其实不少但我实际试过一圈后最后还是选了 SmsForwarder 加 Flask 的组合。先说手机端除了 SmsForwarder还有 Tasker、Auto.js 这类自动化脚本工具也能监听短信Tasker 功能确实强大但它的脚本可读性差改起来麻烦Auto.js 以前挺好用但后续维护情况一般而且为了读一条短信写一大段无障碍脚本性价比太低了。SmsForwarder 的优势在于它是专门为短信转发设计的开源工具界面清楚权限接管成熟自带 Webhook 发送通道我只需要在服务端准备一个 HTTP 接口就行。服务端选 Flask 而不是 FastAPI、Django理由也很直接。这个场景本质上就是“接收一个 POST 请求做点解析落库再给一个查询接口”Flask 写下来整个应用不到 100 行没有额外的心智负担。FastAPI 虽然自带请求校验和自动文档但引入的异步、类型标注概念对只想要一个短信接收接口的人来说是多余的Django 就更不用说了为一个接口起全家桶没必要。SmsForwarder 负责把短信从手机里“搬运”出来Flask 负责把短信内容变成结构化数据这套组合刚好卡在简单和够用之间。1.2 数据链路与部署形态在设计整个方案之前先把数据链路画清楚后面每一步才知道自己在调什么。我实际跑通的链路是这样的运营商把短信送到手机系统短信收件箱SmsForwarder 通过短信读取权限监听新短信按照事先配置的转发规则做过滤命中的短信交给 Webhook 发送通道由 SmsForwarder 向 Flask API 发起一个 HTTP POST 请求Flask 拿到手机号、短信内容和时间信息后用正则把验证码抠出来写入 SQLite 数据库最后外部系统通过一个查询接口把验证码取走。部署形态上有几种做法。刚开始调试时手机和电脑连同一个 WiFi直接用局域网 IP 就能联调这是最快的方式。如果手机在外网环境比如把旧手机插着卡固定放在家里或者人就在外面但手机随时可能切换网络那就必须有一个公网可达的地址。我一般会优先把 API 部署到云服务器省事稳定如果只是想拿家里那台常开的电脑做后端就用 frp 内网穿透把本地端口暴露出去路由器端口映射也能做但运营商经常封 80/443 端口而且自己改路由器配置暴露内网服务风险更大我基本不推荐。1.3 五步搭建法整体看板为了心里有数先把五步对应的内容和产出放在一张表里后面每一步展开讲。步骤核心工作关键产出步骤1Android 端 SmsForwarder 安装、权限与 Webhook 通道配置手机能监听短信并发出 HTTP 请求步骤2Flask API 服务端代码编写可接收、解析、存储、查询验证码的接口步骤3API 部署到公网或局域网可访问地址手机能把请求送达服务端步骤4SmsForwarder 对接 API验证整条链路验证码自动入库并可查询步骤5接口安全加固、进程保活与数据清理服务长期稳定运行2. 步骤1Android 端 SmsForwarder 的安装与配置2.1 安装、权限与保活设置SmsForwarder 我实际用的是 v6.4.0你直接从 GitHub Releases 页面下载对应 APK或者在国内一些应用市场也能搜到。安装完打开第一件事不是去配转发规则而是先把权限给足。短信读取权限是核心没有它什么都监听不到部分 ROM 还会要求单独授权“通知使用权”或者“后台弹出界面”这些最好一次性全部允许。这里要特别提醒一句国内 ROM 的后台管理比原生 Android 激进很多。我遇到过最典型的情况是权限全给了转发规则也配好了但手机锁屏一段时间后SmsForwarder 进程被系统杀掉短信到了却没触发任何转发。解决的办法是把 SmsForwarder 加入电池优化的“不限制”白名单同时允许它自启动再把通知栏常驻打开。部分手机上还要在“最近任务”里把 App 锁定防止被一键清理。手机重启后也建议手动打开一次 App有些系统不会在开机后自动拉起所有广播接收器这一步如果不做重启就等于服务停了。2.2 新建 Webhook 发送通道SmsForwarder 的“发送通道”就是负责把短信内容发出去的出口进去之后点右下角添加选“Webhook”。我一般这样配置请求方式选 POSTURL 先随便填一个占位地址等到后面 Flask 接口部署好再回来改请求头里加一行Content-Type: application/jsonBody 用 JSON 模板常见的变量是%s代表短信正文%ss代表发送方号码所以 Body 填{phone:%ss,content:%s}不同小版本对变量的写法可能有差异如果界面上有“参数说明”入口以软件提示为准。我遇到过有人直接在 Body 里写死字符串导致所有短信进来都是同一条检查了大半天才发现是变量没生效。配置完成后SmsForwarder 一般自带一个“测试发送”按钮点一下如果发送通道能收到请求说明通道本身是通的后面再联调真实短信。2.3 配置转发规则只过滤验证码转发规则的逻辑是满足条件的短信才走发送通道。默认可以全量转发但实际运行起来你会发现垃圾短信、通知短信全进来了数据一多后面提取和查询都变难处理。所以我会在规则里加上关键词过滤常见的就是“验证码”“校验码”“动态码”满足其中任意一个就转发。针对不同 SIM 卡SmsForwarder 也支持在规则里指定卡槽这个在多卡手机上很有用。比如工作卡和生活卡分开我只想把工作卡收到的验证码进自动化流程那就单独建一条规则选择卡1再加上关键词条件。另外如果短信来源是固定的服务号码也可以把发件人号码直接加进规则减少拦截误判。配置完先在“转发记录”里看有没有日志不要直接拿真实的银行验证码测试先用一个自己发的普通短信验证链路更稳妥。3. 步骤2Flask API 服务端代码实现3.1 项目结构与依赖安装服务端代码我不喜欢拆得太零碎这个场景就一个文件就能讲清楚。完整目录结构如下sms-forwarder-api/ ├── app.py └── requirements.txtrequirements.txt里只需要 Flask我本地用的是 Flask 3.0 以上的版本Python 用 3.10 或更高代码里用到的sqlite3是标准库不需要额外安装。flask3.0建议在服务器上用虚拟环境安装避免污染系统 Python 环境后面的 systemd 配置也会用到这个路径。3.2 核心接口代码下面的代码就是我实际在用的简化版本包含了初始化数据库、短信接收接口、验证码提取和最新记录查询接口。你可以直接复制保存为app.py。import os import re import sqlite3 import time from flask import Flask, request, jsonify DATABASE sms.db ACCESS_TOKEN os.getenv(SMS_API_TOKEN, please-change-me) app Flask(__name__) def get_db(): return sqlite3.connect(DATABASE) def init_db(): conn get_db() try: conn.execute( CREATE TABLE IF NOT EXISTS sms_message ( id INTEGER PRIMARY KEY AUTOINCREMENT, phone TEXT NOT NULL, content TEXT NOT NULL, code TEXT DEFAULT , sim TEXT DEFAULT , received_at INTEGER NOT NULL ) ) conn.commit() finally: conn.close() def save_sms(phone, content, code, sim): conn get_db() try: conn.execute( INSERT INTO sms_message (phone, content, code, sim, received_at) VALUES (?, ?, ?, ?, ?), (phone, content, code, sim, int(time.time())), ) conn.commit() finally: conn.close() def extract_code(content): # 优先匹配带关键词的验证码文案 patterns [ r验证码[:\s]*([0-9]{4,8}), r动态码[:\s]*([0-9]{4,8}), r校验码[:\s]*([0-9]{4,8}), ] for pattern in patterns: match re.search(pattern, content, re.IGNORECASE) if match: return match.group(1) # 兜底取连续 6 位数字 match re.search(r\b(\d{6})\b, content) if match: return match.group(1) return app.route(/sms, methods[POST]) def receive_sms(): # 鉴权优先从 Header 取再兼容 query 参数 token request.headers.get(X-Api-Token) or request.args.get(token) if token ! ACCESS_TOKEN: return jsonify({code: 403, message: forbidden}), 403 data request.get_json(silentTrue) if not data: data request.form.to_dict() if not data: return jsonify({code: 400, message: invalid json body}), 400 phone str(data.get(phone, )).strip() content str(data.get(content, )).strip() sim str(data.get(sim, )).strip() if not content: return jsonify({code: 400, message: content is empty}), 400 code extract_code(content) save_sms(phone, content, code, sim) print(f[sms] phone{phone} code{code} content_len{len(content)}) return jsonify({code: 0, message: ok, data: {code: code}}) app.route(/sms/latest, methods[GET]) def latest_sms(): token request.headers.get(X-Api-Token) or request.args.get(token) if token ! ACCESS_TOKEN: return jsonify({code: 403, message: forbidden}), 403 conn get_db() try: row conn.execute( SELECT phone, content, code, sim, received_at FROM sms_message ORDER BY id DESC LIMIT 1 ).fetchone() finally: conn.close() if not row: return jsonify({code: 404, message: no sms yet}), 404 return jsonify({ code: 0, data: { phone: row[0], content: row[1], code: row[2], sim: row[3], received_at: row[4], }, }) app.route(/healthz, methods[GET]) def healthz(): return jsonify({code: 0, message: ok}) if __name__ __main__: init_db() app.run(host0.0.0.0, port5000, debugFalse)3.3 关键逻辑说明Token 这块我重点说一下。代码里SMS_API_TOKEN从环境变量读取避免把密钥硬编码到代码里提交到 Git。这是很多小项目最容易忽略的点以为本地代码不公开就无所谓等代码传到仓库或者发给同事之后就收不住了。request.get_json(silentTrue)的作用是如果请求体不是合法 JSON不会直接抛异常而是返回None程序再尝试从表单数据里取值。之所以要兼容表单是因为 SmsForwarder 不同版本或者不同人配置的发送通道可能用的是application/x-www-form-urlencoded接口兼容两种格式后面联调时能少踩很多坑。extract_code的正则我做了两层第一层匹配“验证码”“动态码”“校验码”这类关键词后面跟 4 到 8 位数字第二层是兜底逻辑当短信文案里没有这些关键词时直接提取整段文本里连续的 6 位数字。实际跑下来这个方法能覆盖国内绝大多数验证码短信格式。返回结构统一用{code: ..., message: ..., data: ...}对接方拿到响应先看code是不是 0逻辑清楚排查问题也方便。3.4 接口参数说明与扩展思路接口方法说明/smsPOSTSmsForwarder 调用的短信上报接口/sms/latestGET外部系统轮询获取最新一条验证码/healthzGET健康检查接口用于进程守护基础版本能跑了之后可以根据自己的需求扩展。比如要接多个业务方可以在/sms的 Body 里加一个source字段要把验证码实时推送到企业微信群或者钉钉群在save_sms之后调用对应的 Webhook 就行如果不想用轮询方式取验证码也可以在/sms接口里直接调用下游业务接口把验证码送到自己的登录脚本里。4. 步骤3把 API 暴露到公网让手机能回调4.1 本地先跑通不要让手机直接上公网我强烈建议第一步先在本机把接口验证清楚再考虑公网部署。本地运行命令很简单export SMS_API_TOKENyour-secret-token python app.py然后另开一个终端用 curl 模拟 SmsForwarder 的请求curl -X POST http://127.0.0.1:5000/sms?tokenyour-secret-token \ -H Content-Type: application/json \ -d {phone:13800138000,content:【测试】验证码1234565分钟内有效。}正常的话会返回{code:0,message:ok,data:{code:123456}}。再访问查询接口curl http://127.0.0.1:5000/sms/latest?tokenyour-secret-token能看到刚才入库的那条记录。这个流程通了说明 Flask 代码本身没问题后面出问题都是网络层和配置层的事排查范围一下子就缩小了。4.2 云服务器部署systemd 托管加 Nginx 反代如果手头有云服务器我推荐直接部署到云上最省心。把代码上传到服务器后在/etc/systemd/system/sms-forwarder-api.service里写一个服务文件[Unit] DescriptionSms Forwarder Flask API Afternetwork.target [Service] Typesimple Userroot WorkingDirectory/opt/sms-forwarder-api EnvironmentSMS_API_TOKENyour-secret-token ExecStart/usr/bin/python3 /opt/sms-forwarder-api/app.py Restartalways RestartSec3 [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable --now sms-forwarder-api这里有一个很多人会漏掉的细节Environment里写的 token 就是 Flask 读的SMS_API_TOKEN部署的时候不要复制我这里的占位符务必改成一个足够长的随机字符串。进程崩溃后会自动重启我现在线上服务跑了大半年基本没有因为进程退出导致服务不可用的情况。如果域名已经备案并且能解析到服务器我再建议加一层 Nginx 反向代理把 80 端口转给本机 5000 端口顺便用 certbot 签一个免费 HTTPS 证书。Nginx 配置核心就一段server { listen 80; server_name sms.example.com; location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }为什么要加 HTTPS因为验证码短信本身就是敏感信息如果走明文 HTTP在公网链路上等于裸奔任何一个中间节点都能看到内容。证书用 Lets Encrypt 免费签就行三分钟的事别省。4.3 frp 内网穿透把家里电脑变成后端如果不想买云服务器或者后端服务本来就在家里电脑上FRP 内网穿透是成本最低的选择。需要一台有公网 IP 的服务器做 frps 服务端然后在家里的电脑上跑 frpc 客户端。frps 服务端配置以较新版本的 TOML 格式为例bindPort 7000 auth.method token auth.token your-frps-tokenfrpc 客户端配置serverAddr 1.2.3.4 serverPort 7000 auth.method token auth.token your-frps-token [[proxies]] name smsapi type tcp localIP 127.0.0.1 localPort 5000 remotePort 25000配置完启动 frpc公网访问地址就变成http://服务器IP:25000/sms。仔细看我的示例remotePort 用的是 25000不是常见的 5000 或者 8080。不用默认端口是为了减少被扫描器乱撞的概率虽然 API 本身有 token 保护但少暴露一点是一点。frp 版本之间的配置格式差异比较大老版本是 INI 风格新版本是 TOML 风格如果启动报错先确认一下版本和配置格式是否匹配。4.4 连不通时先查什么这里分享一个我自己的排查顺序根据出现概率从高到低排先看服务器安全组或者防火墙有没有放行对应端口云服务器经常在系统防火墙之外还有一层安全组最容易漏再确认进程有没有监听在0.0.0.0上如果 Flask 启动时写成127.0.0.1外网必然连不上接着看 Nginx 和 frpc 的日志Nginx 的 access log 能看到请求到底进没进来最后才是看代码逻辑。5. 步骤4SmsForwarder 对接 API 与验证码自动提取5.1 回调地址与 Header 配置Flask 接口部署好之后回到 SmsForwarder 的 Webhook 通道把刚才的占位 URL 改成真实的回调地址。比如云服务器部署就填http://sms.example.com/sms如果用了 frp就填http://服务器IP:25000/smsToken 我建议放在请求头里不要放到 URL 上。因为 URL 会出现在 Nginx access log、frp 日志、SmsForwarder 的转发记录里任何一方日志泄露token 就跟着泄露了。放在 Header 里虽然 Nginx 默认也可能记录一部分请求头但至少减少了一个泄露面。所以我在 SmsForwarder 的 Header 里加上X-Api-Token: your-secret-tokenBody 模板保持之前写的格式不变{phone:%ss,content:%s}5.2 验证码提取规则怎么写才稳从实际收到的短信来看验证码文案五花八门。最常见的是“验证码123456”这种我的extract_code先用带关键词的正则就能匹配到。也有短信写“您的动态码是123456请勿泄露”的“动态码”关键词也能命中。最麻烦的是“本次操作为123456若非本人操作请忽略”这类没有验证码关键词的文案所以代码里加了一个兜底正则提取整条短信里第一组连续的 6 位数字。为什么用 6 位而不是 4 位国内银行和主流互联网平台的验证码绝大多数是 6 位数字用 4 位容易把手机号里的片段误认为是验证码误提率太高。不过如果你主要接收的是某些老系统的 4 位验证码可以把兜底正则可选范围扩大成\d{4,8}但准确性需要自己观察一段时间来判断。还有一点短信里偶尔会有全角冒号或者空格所以正则里写了[:\s]*做兼容这个细节在实际解析时帮了我不少。5.3 实测验证整条链路配置完 SmsForwarder 后不要再走 SmsForwarder 自带的测试按钮那个只能验证发送通道通不通验证不了真实的短信读取和规则匹配。我的做法是直接用自己的另一个手机号给这台手机发一条真实短信内容就写“【测试】验证码123456”。短信到达后稍等一两秒看 Flask 这边的日志正常会输出类似这样的内容[sms] phone13800138000 code123456 content_len22再请求一次/sms/latest能看到那条短信的所有字段已经入库。到这里整条链路就通了。后面如果要对接自己的自动化系统外部脚本只需要定时请求/sms/latest拿到新的验证码之后填入自己的登录或注册流程就行。5.4 多张 SIM 卡怎么区分多卡用户经常问怎么知道验证码是哪张卡收到的SmsForwarder 支持按卡槽绑定不同的发送通道所以我的做法是建两个 Webhook 通道通道 A 的 Body 里加一个固定字段sim:card1在规则里指定卡槽1使用通道 A通道 B 的 Body 里写sim:card2规则指定卡槽2使用通道 B。这样 Flask 入库时sim字段就能区分来源。查询的时候也可以加一个按sim过滤的接口参数需要的话自己扩展两行代码就行。6. 步骤5安全加固与长期稳定运行6.1 接口鉴权与防刷很多从零开始做这个小工具的人会忽略一个问题/sms接口一旦公开到公网理论上任何人都可以往你的服务器上灌数据。单靠一个固定 token 其实是不够的因为 token 一旦泄露防刷就形同虚设。我自己的做法是在 Flask 里再加一层简单的 IP 白名单判断把 SmsForwarder 所在手机的公网出口 IP 或者云服务器内网地址加入白名单其他 IP 一律拒绝。如果手机经常换网络这个策略就需要权衡至少要保证 token 的强度足够。Token 的生成不要用生日、手机号这类可猜测的字符串用openssl rand -hex 32生成长随机串定期更换。更换 token 的时候SmsForwarder 里的 Header 和服务器上的环境变量要同步改建议先在服务器更新完环境变量并重启服务再改 SmsForwarder 配置减少中间态的时间窗口。6.2 数据与日志安全验证码短信属于敏感个人信息存得越多风险越大。我的线上服务每 30 天清一次历史短信用一条 SQL 就能完成DELETE FROM sms_message WHERE received_at strftime(%s, now, -30 days);用 cron 定时跑这条命令就行。日志方面Flask 代码里我故意只打印验证码和内容长度不打印完整短信原文这样即使日志被翻到泄露的信息也有限。SQLite 数据库文件权限也要收一下chmod 600 sms.db是基本操作。如果部署在云服务器上建议把数据库目录和代码目录分开备份时只备份数据库文件不给别人看源代码的机会。6.3 进程守护与手机保活服务端有 systemd 的Restartalways兜底但重启之后如果接口起不来还是要有一个主动检测手段。我在 crontab 里加了一个定时任务每分钟检查一次健康接口* * * * * curl -fsS http://127.0.0.1:5000/healthz || systemctl restart sms-forwarder-api这样即使进程假死、端口被占或者其他异常情况最多一分钟服务就自动恢复了。手机端那一边SmsForwarder 的保活是更大的难点。我现在的做法是找一台不插卡的旧手机插着充电线固定放在办公室或者家里WiFi 连接稳定专门跑 SmsForwarder这样不依赖手机作为日常通讯工具时的各种锁屏限制也没有电池续航焦虑。这台设备加电后SmsForwarder 配上开机自启和后台白名单基本能实现几个月不用碰的稳定运行。7. 常见问题与排查技巧实录7.1 SmsForwarder 收不到短信或者不触发转发遇到这个问题先打开 SmsForwarder 的转发记录看一眼。如果记录里根本没有这条短信说明短信读取环节出了问题重点查系统短信权限有没有真正生效以及手机有没有把 SmsForwarder 进程杀掉。如果记录里有短信但显示“发送失败”那就是发送通道配置有问题回去检查 URL、Header 和 Body 模板。国内 ROM 还有一类比较隐蔽的问题双卡手机上卡槽2 的短信被系统自动归档到“骚扰拦截”或“垃圾短信”文件夹SmsForwarder 默认只监听收件箱的短信这两类短信可能触发不了广播。遇到这种情况把相关号码加入系统联系人或者关闭系统的骚扰拦截功能基本就能解决。7.2 API 返回 400 和 403 的处理400绝大多数情况下是请求体不是合法 JSON或者 JSON 里的字段名和代码不一致。SmsForwarder 端显示收到 400 时先手动用 curl 模拟请求确认接口本身是好的再检查 Body 模板里的变量有没有被正确替换。有些版本的 SmsForwarder 在 Body 里直接用字符串拼接如果短信内容里包含引号或者特殊字符JSON 就可能被破坏导致服务端解析失败。这时建议把变量换成系统支持的其他占位符或者检查通道配置里有没有专门处理转义的选项。这里特别提醒网上搜索“API 400 错误”时经常会看到类似于api error: 400 invalid schema for function artifact这样的报错那是大模型 API 调用时 function schema 不合法导致的和我们这个 Flask 接口的 400 完全是两回事。遇到 Flask 报 400不要套用那类排查思路老老实实看请求体格式和字段名。403就是 token 不对或者 IP 不在白名单里先确认 SmsForwarder 请求头里的 token 和服务器环境变量是否一致再看是不是换了网络导致出口 IP 变化。7.3 手机显示发送成功但 Flask 没收到SmsForwarder 的“发送成功”只代表请求从手机发出去了不代表服务端真的接收并处理了。先看 Flask 进程日志如果没有任何输出说明请求根本没到应用层。按这个顺序排查在服务器上用tcpdump或者 Nginx access log 确认请求有没有进来确认防火墙和安全组放行了对应端口如果用了 frp看 frpc 客户端日志有没有建立连接还有一步很容易漏服务器上 Flask 绑定的地址如果是默认127.0.0.1外网请求永远进不来检查启动配置里有没有写host0.0.0.0。7.4 验证码提取为空或者提取错误这是最让人头疼的问题因为网络都通了就是拿到的验证码不对。先用/sms/latest接口看看content字段里存的原始短信内容是什么是不是 SmsForwarder 没把短信正文完整传过来。如果内容完整但验证码为空就是正则没匹配上根据原始文案调整extract_code里的正则。有一种比较常见的情况是短信里验证码和数字混合在一起比如“验证码A1B2C3”这种纯数字正则是匹配不到的需要自己补一个字母数字混合的正则。还有一种容易被忽略的情况系统自带的垃圾短信拦截在短信到达收件箱之前就拦掉了SmsForwarder 自然监听不到。如果某些平台的短信一直收不到先把系统的智能拦截功能关闭再测试一次。7.5 同一条验证码被多次处理SmsForwarder 在弱网环境或者发送通道响应慢的时候会自动重试加上手机信号切换等不可控因素同一短信被重复投递是真实存在的情况。我的解决办法是服务端加一个时间窗口去重同一个手机号、同一条内容在 60 秒内重复上报就忽略。SQL 层面写起来不复杂在save_sms之前先查一下最近 60 秒内有没有相同phone和content的记录有就直接返回成功但不落库。最终效果是即使手机重试三次业务层只会收到一条验证码不会因为重复消费导致下游流程执行多次。7.6 服务重启后数据遇到丢失如果是正常用 SQLite 落库重启不会丢数据。如果发现重启后/sms/latest查不到记录大概率是你改代码的时候用了内存列表存储也就是把短信存在了 Python 进程的全局变量里。这种方案进程一重启就全没了虽然代码简单但生产环境不能用。确认代码里是 SQLite 读写逻辑同时定期备份sms.db文件云服务器上做快照或者直接scp拉到本地都行。最后聊几句实操心得整个方案跑了大半年之后我最大的体会是手机端保活比服务端难搞得多。服务端只要写了 systemd 文件进程崩了自动拉起来基本不用管但手机端不同国内各种 ROM 的后台策略五花八门同一个版本SmsForwarder在 A 手机上正常在 B 手机上就可能被系统杀掉。最省心的做法就是找一台旧手机专机专用不插日常使用的卡不装乱七八糟的 App固定插电放家里跑这个服务它能稳定运行很久。另外一个建议是 token 能放 Header 就不要放 URL因为 URL 会出现在 Nginx 日志、frp 日志、SmsForwarder 的转发记录里泄露面实在太大。还有一个小工具思路可以顺手加上在/sms接口里解析完验证码后直接调一次企业微信或者钉钉的群机器人 Webhook验证码实时推送到群里这样即使你不在电脑前手机上也能第一时间看到验证码内容。整体来说这套组合代码量不大关键是把每一环的日志和验证做扎实一次性把链路跑通之后后面基本不需要再动它。
返回列表