目录
前言
了解流程
介绍
快速学习与测试
绑定手机号
调用API发送短信
SDK调用API
安装SDK
复制代码
替换AccessKeyId和AccessKeySecret
编辑
说明
常见验证码流程
工具类
使用示例
配置文件
前言
我们使用阿里云短信SDK来进行发短信,目前SDK V1.0不再维护,现在使用的V2.0版本。
了解流程
一条短信由短信签名和短信模板组成,因此在发送短信前,您需要先完成短信资质以及签名、模板
的申请工作,并等待审核通过。通过模板变量自定义,您可以实现短信内容的定制化。
运营商实名报备流程平均需要5-7个工作日,基于近期观测,部分运营商实名报备流程需要7-10个
工作日,但运营商未对此时效进行承诺,实际可能需要更长时间。建议您合理规划业务并提前申请
相关资质和签名,以确保在正式使用前有充足的时间完成实名报备。
介绍
短信服务官方文档:短信服务(SMS)-阿里云帮助中心
开通阿里云短信服务点击国内消息有资质管理、签名管理、模板管理(这个要企业资质才能玩)正常就按照这个顺序配置 资质—>签名—>模板
快速学习与测试
由于我们没有企业资质,所以只能使用快速学习与测试功能,来调用API发送短信。
绑定手机号
在这里我们绑定一个手机号,用于后续发送验证码。
调用API发送短信
选择【专用】测试签名/模板 点击调用API发送短信
点击发起调用 可以看到响应报文
刚刚绑定的测试手机号就可以收到测试验证码
SDK调用API
安装SDK
找到SDK实例 点击SDK安装信息 获取maven依赖 添加进我们项目中。
复制代码
选择SDK代系V2.0 语言选择Java(也可以选择异步),复制代码信息。
替换AccessKeyId和AccessKeySecret
由于工程代码使用的是更安全的无AK方式,所以我们要改造一下代码,使用我们自己配置的AK。
然后运行代码发送短信即可。
Config config = new Config();
config.setAccessKeyId("xxxxx");
config.setAccessKeySecret("xxxxx");
return new Client(config);
说明
后续开发只需要把AK替换成自己的,配置短信签名、模板代码、手机号码、模板变量即可。
有问题可以查看官网文档:短信服务_SDK中心-阿里云OpenAPI开发者门户
常见验证码流程
一般发送验证码,由服务端生成验证码存储在Redis中,存储在Redis中5分钟内有效,调用阿里云
短信发送验证码,用户输入验证码从Redis拿到验证码进行校验,一般只能1分钟发送一次验证码,
如果1分钟后重新获取之前验证码失效。
代码实现参考:
public void handleSmsFlow(String phone) {
// 1.频率控制检查
if (redisTemplate.hasKey(buildCoolingKey(phone))) {
throw new BusinessException("操作过于频繁");
}
// 2.生成&存储验证码
String code = generateRandomCode();
redisTemplate.opsForValue().set(buildCodeKey(phone), code, 5, MINUTES);
redisTemplate.opsForValue().set(buildCoolingKey(phone), "", 1, MINUTES);
// 3.异步发送短信
CompletableFuture.runAsync(() -> {
try {
smsClient.sendVerificationCode(phone, code);
} catch (Exception e) {
alertService.notifySmsFailure(phone, code);
}
}, smsThreadPool);
}
private String buildCoolingKey(String phone) {
return "sms:cooling:" + phone;
}
private String buildCodeKey(String phone) {
return "sms:code:" + phone;
}
工具类
使用AI对技术代码进行工具类封装。
package com.taoran.sms.util;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.aliyun.tea.TeaException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
/**
* 阿里云短信服务工具类[3,6](@ref)
* 功能特性:
* 1. 自动加载云资源配置
* 2. 动态模板参数支持
* 3. 异常重试机制
* 4. 全链路日志追踪
*/
@Slf4j
@Component
public class AliyunSmsUtil {
// 通过@Value注入配置参数(生产环境推荐使用配置中心)
@Value("${aliyun.sms.access-key-id}")
private String accessKeyId;
@Value("${aliyun.sms.access-key-secret}")
private String accessKeySecret;
@Value("${aliyun.sms.sign-name}")
private String signName;
@Value("${aliyun.sms.endpoint}")
private String endpoint = "dysmsapi.aliyuncs.com"; // 默认值
private Client client;
// 初始化客户端[6](@ref)
@PostConstruct
private void initClient() throws Exception {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret);
config.endpoint = endpoint;
this.client = new Client(config);
log.info("阿里云短信客户端初始化完成,Endpoint: {}", endpoint);
}
/**
* 通用短信发送方法[3,6](@ref)
* @param phoneNumbers 接收号码(多个用逗号分隔)
* @param templateCode 模板CODE
* @param templateParams 模板参数键值对
* @return 发送结果
*/
public SendSmsResponse send(String phoneNumbers,
String templateCode,
Map
try {
SendSmsRequest request = buildRequest(phoneNumbers, templateCode, templateParams);
return executeSend(request);
} catch (Exception e) {
log.error("短信发送异常 | phones:{} | template:{}", phoneNumbers, templateCode, e);
return null;
}
}
// 构建请求对象[6](@ref)
private SendSmsRequest buildRequest(String phoneNumbers,
String templateCode,
Map
return new SendSmsRequest()
.setPhoneNumbers(phoneNumbers)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam(convertToJson(params));
}
// 执行发送操作[6](@ref)
private SendSmsResponse executeSend(SendSmsRequest request) throws Exception {
RuntimeOptions runtime = new RuntimeOptions();
try {
SendSmsResponse response = client.sendSmsWithOptions(request, runtime);
log.debug("短信发送成功 | RequestId:{}", response.getBody().requestId);
return response;
} catch (TeaException e) {
handleTeaException(e);
throw new RuntimeException("阿里云服务异常", e);
}
}
// 异常处理[6](@ref)
private void handleTeaException(TeaException e) {
log.error("阿里云API异常 || Code:{} | Message:{} | Recommend:{}",
e.getCode(), e.getMessage(), e.getData().get("Recommend"));
}
// Map转JSON工具[3](@ref)
private String convertToJson(Map
if (params == null || params.isEmpty()) return "{}";
StringBuilder json = new StringBuilder("{");
params.forEach((k, v) ->
json.append("\"").append(k).append("\":\"").append(v).append("\","));
json.deleteCharAt(json.length()-1).append("}");
return json.toString();
}
}
使用示例
// 在Spring Boot控制器中使用
@RestController
@RequestMapping("/sms")
public class SmsController {
@Autowired
private AliyunSmsUtil smsUtil;
@PostMapping("/send-verification")
public ResponseEntity> sendVerification(@RequestParam String phone) {
Map
SendSmsResponse response = smsUtil.send(
phone,
"SMS_154950909",
params
);
if (response != null && "OK".equals(response.getBody().getCode())) {
return ResponseEntity.ok("验证码发送成功");
}
return ResponseEntity.status(500).body("发送失败");
}
private String generateRandomCode(int length) {
// 生成随机验证码逻辑[6](@ref)
}
}
配置文件
aliyun:
sms:
access-key-id: xxxxx
access-key-secret: xxxx
sign-name: 阿里云短信测试
endpoint: dysmsapi.aliyuncs.com