1. 接入阿里云短信服务
登录阿里云服务器官网,然后点击搜索输入短信服务,点击免费开通
点击快速学习与测试
测试用的所以点击专用测试签名模板,并点击调用 API 测试
2. 添加阿里云 SDK
然后进入到另一个页面后,点击 SDK 详情,这里使用的是 SpringBoot,所以点击 java 实例
在旁边有个 SDK,点击进去,就可以看到你们的 maven 情况,然后把这个 maven 依赖引入到 SpringBoot 项目中的 pom.xml 文件中
// 省略...
<dependencies>
// 省略...
<!-- 阿里云短信发送 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
</dependency>
</dependencies>
// 省略...
3. 添加 AccessKey
查看发送短信的示例代码,你会发现需要填写阿里云的 Access Key,它是接入凭证。点击回到阿里云首页,将鼠标移动到登录用户的头像上,即可看到 AccessKey 选项,点击即可查看:
记得给你的账号充值一点钱,比如 1 块钱,因为等会发送测试短信需要费用。
然后编辑你的 application.yml文件,把这些 accesskey填写进去
aliyun:# 接入阿里云(发送短信使用)
accessKeyId:xxx# 填写你自己的
accessKeySecret:xxx# 填写你自己的
写个配置类来读取yml中的配置信息,AliyunAccessKeyProperties 配置类
@ConfigurationProperties(prefix = "aliyun")
@Component
@Data
public class AliyunAccessKeyProperties {
private String accessKeyId;
private String accessKeySecret;
}
然后,新建 AliyunSmsClientConfig 配置类,用于初始化一个短信发送客户端,注入到 Spring 容器中,以便后续使用,这里对官方的实例代码进行了改造:
@Configuration
@Slf4j
public class AliyunSmsClientConfig {
@Resource
private AliyunAccessKeyProperties aliyunAccessKeyProperties;
@Bean
public Client smsClient() {
try {
Config config = new Config()
// 必填
.setAccessKeyId(aliyunAccessKeyProperties.getAccessKeyId())
// 必填
.setAccessKeySecret(aliyunAccessKeyProperties.getAccessKeySecret());
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.endpoint = "dysmsapi.aliyuncs.com";
return new Client(config);
} catch (Exception e) {
log.error("初始化阿里云短信发送客户端错误: ", e);
return null;
}
}
}
最后,再创建一个 AliyunSmsHelper 短信发送工具类,代码如下:
@Component
@Slf4j
public class AliyunSmsHelper {
@Resource
private Client client;
/**
* 发送短信
* @param signName
* @param templateCode
* @param phone
* @param templateParam
* @return
*/
public boolean sendMessage(String signName, String templateCode, String phone, String templateParam) {
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName(signName)
.setTemplateCode(templateCode)
.setPhoneNumbers(phone)
.setTemplateParam(templateParam);
RuntimeOptions runtime = new RuntimeOptions();
try {
log.info("==> 开始短信发送, phone: {}, signName: {}, templateCode: {}, templateParam: {}", phone, signName, templateCode, templateParam);
// 发送短信
SendSmsResponse response = client.sendSmsWithOptions(sendSmsRequest, runtime);
log.info("==> 短信发送成功, response: {}", JsonUtils.toJsonString(response));
return true;
} catch (Exception error) {
log.error("==> 短信发送错误: ", error);
return false;
}
}
}
4. 业务层异步发送短信
一般情况下,在用户调用发送短信接口后,在业务代码不出错的情况下都会先提示验证码已经发送成功,所以一般都会先返回信息,然后用多线程中的异步去给用户发送短信
1、多线程的自定义
@Configuration
public class ThreadPoolConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(50);
// 队列容量
executor.setQueueCapacity(200);
// 线程活跃时间(秒)
executor.setKeepAliveSeconds(30);
// 线程名前缀
executor.setThreadNamePrefix("AuthExecutor-");
// 拒绝策略:由调用线程处理(一般为主线程)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是被没有完成的任务阻塞
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
2、异步发送短信,这里未来自适应我的项目中的短信登录接口,所以额外加了一些逻辑判断
/**
* 发送短信验证码
*
* @param sendVerificationCodeReqVO
* @return
*/
@Override
public Response<?> send(SendVerificationCodeReqVO sendVerificationCodeReqVO) {
// 手机号
String phone = sendVerificationCodeReqVO.getPhone();
// 构建验证码 redis key
String key = RedisKeyConstants.buildVerificationCodeKey(phone);
// 判断是否已发送验证码
boolean isSent = redisTemplate.hasKey(key);
if (isSent) {
// 若之前发送的验证码未过期,则提示发送频繁
throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_SEND_FREQUENTLY);
}
// 生成 6 位随机数字验证码
String verificationCode = RandomUtil.randomNumbers(6);
log.info("==> 手机号: {}, 已生成验证码:【{}】", phone, verificationCode);
// 调用第三方短信发送服务
threadPoolTaskExecutor.submit(() -> {
String signName = "阿里云短信测试";
String templateCode = "SMS_154950909";
String templateParam = String.format("{"code":"%s"}", verificationCode);
aliyunSmsHelper.sendMessage(signName, templateCode, phone, templateParam);
});
// 存储验证码到 redis, 并设置过期时间为 3 分钟
redisTemplate.opsForValue().set(key, verificationCode, 3, TimeUnit.MINUTES);
return Response.success();
}