30分钟用闲置安卓手机搭建私有短信网关
本教程演示如何用一台闲置安卓手机和 httpSMS 快速搭建零成本私有短信服务。通过 HTTP API 发送验证码、配置 Webhook 接收短信回调,完整覆盖从手机配置到 Spring Boot 集成的全流程。无需备案、无需第三方平台,适合个人项目、MVP 开发和物联网告警场景。

30分钟用闲置安卓手机搭建私有短信网关
做后端开发的应该都遇到过这种场景:系统需要给用户发短信验证码、物联网设备异常需要短信告警、或者你想在服务器上跑个脚本给自己发个通知。找第三方 SMS 服务吧,国内要备案、国外要绑信用卡,按条收费还不便宜;自己搞短信猫硬件吧,成本高、维护麻烦。
有没有一种办法,用一台闲置的安卓手机就能搞定?
今天带你实操开源项目 httpsms,把 Android 手机变成 SMS 网关。只需调用 HTTP API 即可发短信,手机收到短信还能通过 Webhook 回调到服务器。整条链路跑通后,你就拥有了一套零成本的私有短信服务。
前置准备
- 一台闲置或主力 Android 手机(Android 5.0+,能插 SIM 卡即可)
- 一张有短信功能的 SIM 卡(预存话费,国内短信几分钱一条)
- 一个后端项目(本教程用 Java/Spring Boot 演示)
- 公网域名或内网穿透工具(如 ngrok,用于 Webhook 回调)
整篇教程以「使用官方托管服务」为主线,无需自部署服务端,10 分钟即可跑通。
第一步:注册并获取 API Key
打开 httpsms.com 注册账号,进入 Settings 页面找到 API Key。这个 Key 是调用所有 API 的身份凭证,等同于云服务的 AccessKey,务必妥善保管。
httpSMS 的后端 API 是无状态的,通过 API Key 识别用户并定位绑定的手机设备。一个 Key 对应一个用户下的所有关联手机。
第二步:安装并配置 Android 端
这是唯一需要手动操作的硬件环节。
- 从 官方发布页 或 官网直达 下载 APK 并安装。
- 打开 App,用注册账号登录。
- 授权以下权限:
- 短信权限:读取发送状态回执、拦截收到的短信进行转发
- 通知权限:接收推送通知,触发发送行为
- 电池优化白名单:确保后台不被系统杀死
踩坑提醒: 国产 ROM(MIUI、ColorOS、HarmonyOS 等)默认对后台应用做激进的进程管理。装完后务必到手机设置 → 电池 → 应用启动管理,将 httpSMS 设为「允许后台运行」和「自启动」,否则会出现消息发不出去或回调丢失。
App 登录成功后显示设备在线标识,此时手机已与账户绑定,API 端可以开始调度。
第三步:发送第一条短信
httpSMS 的 API 非常干净,核心就一个发送端点。用 curl 快速测试:
bash
curl -X POST "https://api.httpsms.com/v1/messages/send" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "这是一条来自 httpSMS 的测试短信",
"from": "+8613800138000",
"to": "+8613900139000"
}'
替换 YOUR_API_KEY 为你在 Settings 里看到的 Key,from 填你手机上的号码,to 填接收方号码。请求发出后会收到 202 Accepted 响应,说明请求已被接收,手机稍后会把短信实际发出。
为什么返回 202 而不是 200? httpSMS 采用异步架构:API 收到请求后先返回接受状态,再通过 Firebase Push 通知安卓手机,手机收到通知后从 API 拉取消息内容,最后通过 Android 原生 SMS API 发送。这种设计解耦了 API 层和设备层,手机暂时离线也不会阻塞服务端请求。
第四步:实战 —— 用 Spring Boot 搭建短信验证码服务
4.1 项目依赖
Spring Boot 项目引入 WebClient 即可,无需额外 SDK:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
4.2 短信发送服务
java
@Service
public class HttpSmsService {
private static final String API_BASE = "https://api.httpsms.com/v1";
private final WebClient webClient;
@Value("${httpsms.api-key}")
private String apiKey;
@Value("${httpsms.phone-number}")
private String phoneNumber; // +8613800138000
public HttpSmsService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl(API_BASE)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("x-api-key", apiKey)
.build();
}
/**
* 发送短信验证码
*/
public Mono<Void> sendVerificationCode(String targetPhone, String code) {
String content = String.format("【你的应用】您的验证码是 %s,请在 5 分钟内完成验证。", code);
Map<String, String> body = Map.of(
"content", content,
"from", phoneNumber,
"to", targetPhone
);
return webClient.post()
.uri("/messages/send")
.bodyValue(body)
.retrieve()
.bodyToMono(JsonNode.class)
.doOnSuccess(r -> log.info("短信已提交,目标: {}, 状态: {}", targetPhone, r))
.then();
}
}
4.3 Controller 调用
java
@RestController
@RequestMapping("/api")
public class UserController {
private final HttpSmsService smsService;
private final StringRedisTemplate redisTemplate;
// 省略构造器注入...
@PostMapping("/register/send-code")
public ResponseEntity<?> sendCode(@RequestParam String phone) {
String code = String.valueOf(ThreadLocalRandom.current().nextInt(100000, 999999));
// 验证码存入 Redis,5 分钟过期
redisTemplate.opsForValue().set("sms:code:" + phone, code, 5, TimeUnit.MINUTES);
smsService.sendVerificationCode(phone, code)
.subscribe(); // 异步发送,不阻塞接口响应
return ResponseEntity.ok(Map.of("message", "验证码已发送"));
}
}
在 application.yml 配置 Key 和手机号:
yaml
httpsms:
api-key: "YOUR_API_KEY_HERE"
phone-number: "+8613800138000"
调用 /api/register/send-code?phone=+86150xxxx1234,闲置手机就会把验证码短信发出。整个过程完全走自己的线路,不依赖第三方短信平台。
第五步:接收短信 —— Webhook 回调处理
手机收到的短信可通过 Webhook 转发到服务器,适用于「用户上行短信触发业务逻辑」场景,如回复 TD 退订、回复关键词触发查询等。
5.1 配置 Webhook
在 httpsms.com 的 Settings 页面找到 Webhook URL,填入服务端回调地址:
https://your-domain.com/api/sms/callback
本地开发可用 ngrok 做临时公网隧道:
ngrok http 8080,然后把生成的公网地址填进去。
5.2 回调处理代码
java
@PostMapping("/sms/callback")
public ResponseEntity<?> handleSmsWebhook(@RequestBody Map<String, Object> payload) {
log.info("收到短信回调: {}", payload);
String from = (String) payload.get("from");
String content = (String) payload.get("content");
if ("TD".equalsIgnoreCase(content.trim())) {
log.info("用户 {} 请求退订", from);
// 执行业务逻辑...
}
return ResponseEntity.ok(Map.of("status", "received"));
}
手机收到新短信时,httpSMS 会第一时间 POST 到这个地址,payload 包含 from、to、content 等字段。确保回调接口能正常返回 200 状态码即可。
重试机制: 回调返回非 2xx 状态码时,httpSMS 会重试。建议回调接口做好幂等性处理。
常见问题与踩坑提醒
-
消息发不出去/长时间 pending
- 查 App 内日志,确认消息状态是
sent、pending还是expired - 确认手机网络畅通(需能收到 Firebase Push 通知)
- 检查电池优化/自启动权限是否已放开
- 查 App 内日志,确认消息状态是
-
Webhook 没有触发
- 确认 Webhook URL 公网可达(本地用 ngrok)
- 确认 URL 返回 200,4xx/5xx 会触发重试,重试满后放弃
- Settings 里可查看最近的 Webhook 投递记录
-
长短信处理
- Android 原生 SMS 单条短信有字数限制(约 70 字符中文),超长会自动拆分。httpSMS 发送时自动处理拆分,建议控制单条内容在合理范围内以控制成本。
-
费用说明
- httpSMS 官方托管服务有免费额度,超出后按用量付费,具体见官网定价。
- 自部署方案参考 官方文档 的 Docker 部署部分。
总结
用一台安卓手机 + httpSMS,即可从零搭建完整的私有短信服务:
- 注册账号,获取 API Key
- 手机安装 App、登录、放开后台权限
- 调用
/v1/messages/send发短信 - 配置 Webhook URL 接收上行短信
方案没有复杂中间件,无需备案,无需对接运营商 API,拿来就能用。适合个人项目、MVP 产品、内部通知、物联网告警等场景。
下一步可以尝试:
- 使用官方提供的 Go SDK 或 Node SDK
- Webhook 回调结合自动化框架:收到短信 → 触发 CI/CD → 执行部署
- 了解 httpSMS 的速率限制(Back Pressure)和消息过期功能,在生产环境中更稳地控制发送节奏
有问题欢迎在评论区交流,或到仓库提 issue。