用Java案例实现验证码短信发送的完整指南
📖 目录导读
为什么你需要掌握验证码短信发送
在当今互联网应用中,短信验证码已成为用户注册、登录、找回密码等场景的标配安全机制,Java开发者通过短信接口实现验证码发送,不仅能保障业务安全,还能提升用户体验,新手常遇到以下痛点:

- API对接复杂:不同短信服务商(阿里云、腾讯云、容联云)的SDK和签名规则各不相同。
- 安全陷阱:验证码容易被截获、重复使用,或发送频率过高导致服务商封禁。
- 性能问题:高并发下大量HTTP请求阻塞主线程。
本文将指导你用Java案例实现3种主流短信服务的验证码发送,并解决上述问题。
准备工作:注册API和Maven依赖配置
1 注册短信服务商
选择以下任一平台注册账号并获取必要参数:
- 阿里云短信:获取AccessKey、AccessSecret、短信签名、模板CODE。
- 腾讯云短信:获取SecretId、SecretKey、SDK AppID、SignName。
- 聚合数据:免费额度高,仅需API Key(适用于个人项目)。
2 Maven依赖(以阿里云为例)
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>2.2.0</version>
</dependency>
注意:腾讯云需引用
tencentcloud-sdk-java模块,聚合数据只需引入OkHttp(用于GET请求)。
核心代码实现:三大主流案例
1 案例一:阿里云短信验证码(推荐企业级)
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.profile.DefaultProfile;
import java.util.Random;
public class AliSmsSender {
private static final String ACCESS_KEY_ID = "你的AccessKeyId";
private static final String ACCESS_KEY_SECRET = "你的AccessKeySecret";
private static final String SIGN_NAME = "你的短信签名"; // 需在阿里云审核
private static final String TEMPLATE_CODE = "SMS_123456789"; // 模板ID
public static String sendCode(String phone) {
String code = String.valueOf(new Random().nextInt(899999) + 100000);
try {
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
DefaultAcsClient client = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(phone);
request.setSignName(SIGN_NAME);
request.setTemplateCode(TEMPLATE_CODE);
request.setTemplateParam("{\"code\":\"" + code + "\"}");
SendSmsResponse response = client.getAcsResponse(request);
if ("OK".equals(response.getCode())) {
System.out.println("短信发送成功,验证码:" + code);
return code;
} else {
System.err.println("发送失败,原因:" + response.getMessage());
}
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}
2 案例二:腾讯云短信验证码(适合微信生态)
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
public class TencentSmsSender {
public static String sendCode(String phone) {
String code = generateCode();
try {
Credential cred = new Credential("SecretId", "SecretKey");
SmsClient client = new SmsClient(cred, "ap-guangzhou");
SendSmsRequest req = new SendSmsRequest();
req.setSmsSdkAppId("1400XXXXXX");
req.setSignName("你的签名");
req.setTemplateId("1831XXXX");
req.setPhoneNumberSet(new String[]{"+86" + phone});
req.setTemplateParamSet(new String[]{code});
client.SendSms(req);
return code;
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}
3 案例三:聚合数据(免费版,个人项目首选)
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class JuheSmsSender {
private static final String API_KEY = "你的APIKEY";
private static final String URL = "http://v.juhe.cn/sms/send?mobile=%s&tpl_id=1234&tpl_value=%%23code%%23=%s&key=%s";
public static String sendCode(String phone) {
String code = generateCode();
try {
OkHttpClient client = new OkHttpClient();
String requestUrl = String.format(URL, phone, code, API_KEY);
Request request = new Request.Builder().url(requestUrl).get().build();
Response response = client.newCall(request).execute();
String result = response.body().string();
// 解析JSON (此处简化)
System.out.println("聚合数据返回:" + result);
return code;
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}
常见问题问答(Q&A)
Q1:验证码发送失败最常见的原因是什么?
A:① 签名未审核:阿里云/腾讯云的短信签名需提前在后台提交审核(1-2工作日);② 模板参数错误:TemplateParam JSON格式必须严格匹配模板({"code":"123456"});③ 手机号被运营商拦截:建议使用“加白名单”测试。
Q2:如何防止验证码被暴力破解?
A:后端必须进行三重限制:
- 1分钟内同一手机号只能请求1次(使用Redis计数);
- 验证码有效期设为5分钟;
- 输入错误3次后锁定该验证码。
Q3:高并发下如何避免阻塞?
A:使用线程池(如 ThreadPoolExecutor)或 Spring @Async 注解异步调用短信接口,同时用 MQ队列 削峰。
Q4:验证码应该存数据库还是Redis?
A:必须存Redis(内存数据库),因为:
- 设置TTL(过期时间)自清理;
- 读写性能是数据库的100倍;
- 支持分布式环境共享。
性能与安全优化建议
1 核心验证码逻辑(Redis实现)
// 生成并存储验证码(5分钟有效)
redisTemplate.opsForValue().set("sms:code:" + phone, code, 5, TimeUnit.MINUTES);
// 校验时删除(防重复使用)
String stored = redisTemplate.opsForValue().get("sms:code:" + phone);
if (stored != null && stored.equals(inputCode)) {
redisTemplate.delete("sms:code:" + phone);
return true;
}
2 发送频率控制(防刷接口)
// 1分钟内不可重复发送
Boolean canSend = redisTemplate.hasKey("sms:limit:" + phone);
if (canSend) throw new RuntimeException("请60秒后再试");
redisTemplate.opsForValue().set("sms:limit:" + phone, "1", 1, TimeUnit.MINUTES);
3 安全最佳实践
- 不暴露API密钥:在
application.yml中配置并通过@Value注入。 - HTTPS强制:避免验证码在中间人攻击中被截获。
- 日志脱敏:打印日志时遮盖手机号中间四位(如138****1234)。
通过以上三大案例+性能优化,你已经掌握了用Java实现验证码短信发送的完整方案。线上环境务必使用异步+Redis+频率控制,并优先选择阿里云或腾讯云这类稳定服务商。