前置条件
1.创建SpringBoot应用
2.创建短信服务
引入依赖
引入阿里云服务
<!-- aliyun 短信 --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><!-- 注:如提示报错,先升级基础包版,无法解决可联系技术支持 --><version>4.0.6</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.1.0</version></dependency>
application.yml 配置文件
# 短信配置 ly: sms: accessKeyId: xxxx accessKeySecret: xxxxx signName: xx商城 verifyTemplateCode: SMS_1xxxx # 短信模板名称
编写短信工具
SmsConfig.class) (publicclassSmsUtils { privateSmsConfigsmsConfig; privateStringRedisTemplateredisTemplate; //短信API产品名称(短信产品名固定,无需修改)privatefinalStringproduct="Dysmsapi"; //短信API产品域名(接口地址固定,无需修改)privatefinalStringdomain="dysmsapi.aliyuncs.com"; //发送短信最小间隔(分钟)privatestaticfinalLongSMS_MIN_INTERVAL_MINUTE=60000L; //短信手机号保存到 Redis 中的前缀privatestaticfinalStringSMS_PER="sms:phone:"; /*** 发送短信* @param phoneNumber 手机号码* @param signName 短信签名* @param templateCode 短信模板code* @param templateParam 短信模板参数* @return SendSmsResponse 响应体* @throws ClientException*/publicSendSmsResponsesendSms(StringphoneNumber, StringsignName, StringtemplateCode, StringtemplateParam) { // 限流,限制1分钟内不能重复发送短信StringverifyTimeStr=redisTemplate.opsForValue().get(phoneNumber); if (StringUtils.isNotBlank(verifyTimeStr)){ LongverifyTime=Long.valueOf(verifyTimeStr); if (System.currentTimeMillis() -verifyTime<SMS_MIN_INTERVAL_MINUTE){ returnnull; } } try{ //设置超时时间-可自行调整System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化ascClient,暂时不支持多region(请勿修改)IClientProfileprofile=DefaultProfile.getProfile("cn-hangzhou", smsConfig.getAccessKeyId(), smsConfig.getAccessKeySecret()); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClientacsClient=newDefaultAcsClient(profile); //组装请求对象SendSmsRequestrequest=newSendSmsRequest(); //使用post提交request.setMethod(MethodType.POST); //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”request.setPhoneNumbers(phoneNumber); //必填:短信签名-可在短信控制台中找到request.setSignName(signName); //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版request.setTemplateCode(templateCode); //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为//友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败request.setTemplateParam(templateParam); //请求失败这里会抛ClientException异常SendSmsResponseresponse=acsClient.getAcsResponse(request); if (!StringUtils.equals("OK", response.getCode())){ log.info("[短信服务] 发送短息失败,手机号码:{} ,原因:{}", phoneNumber, response.getMessage()); } // 发送短信成功,写入redis,设置生存时间为1分钟redisTemplate.opsForValue().set(SMS_PER+phoneNumber, String.valueOf(System.currentTimeMillis()), 1, TimeUnit.MINUTES); returnresponse; }catch (Exceptione){ log.error("[短信服务] 发送短息异常,手机号码:{}", phoneNumber, e); returnnull; } } }
编写消息监听器
/*** 短信MQ监听器*/SmsConfig.class) (publicclassSmsListener { privateSmsUtilssmsUtils; privateSmsConfigsmsConfig; bindings= ( (value= (value="sms.verify.code.queue", durable="true"), exchange= (value="ly.sms.exchange", type=ExchangeTypes.TOPIC), key="sms.verify.code" )) publicvoidlistenerVerifyCode(Map<String, Object>map) { System.out.println("listenerVerifyCode"); StringphoneNumber= (String) map.remove("phoneNumber"); if (StringUtils.isBlank(phoneNumber)){ return; } smsUtils.sendSms(phoneNumber, smsConfig.getSignName(), smsConfig.getVerifyTemplateCode(), JsonUtils.serialize(map)); } }
单元测试
SpringRunner.class) (publicclassSmsListenerTest { privateAmqpTemplateamqpTemplate; /*** 测试短信验证码*/publicvoidlistenerVerifyCode() throwsInterruptedException { Map<String, Object>msg=newHashMap<>(); msg.put("phoneNumber", "13542898491"); msg.put("code", "0754"); amqpTemplate.convertAndSend("ly.sms.exchange", "sms.verify.code", msg); Thread.sleep(10000); } }