java 阿里雲接口實現發送短信驗證碼
前言–很久沒更新過東西了,突然想起來短信,回顧了一下之前的小項目,把阿里短信的內容簡要講述一下.
1. 阿里雲後臺配置短信相關
1.1 開通短信服務
打開官網,直接搜索短信服務,點擊短信控制檯進入開通短信服務。
阿里短信服務:https://www.aliyun.com/product/sms
1.2 添加模板簽名
開通之後,配置模板及簽名,可根據自己需求配置,審覈時間官方給得:
一般模板預計2小時內審覈完成,審覈通過後可使用。工作時間:9:00-23:00 (法定節日順延),建議您儘量在18:00前提交申請。
1.3 創建祕鑰
AccessKey ID:***************
AccessKey Secret:**********
(建議保存好,很關鍵得一個AK,具有該賬戶完全的權限)
1.4 短信需要後臺授權–注意點
(之前遇到得一個問題)沒授權報錯,發送失敗
2 java–簡單實現短信驗證碼發送
2.1 引入pom依賴
阿里短信服務文檔、sdk下載https://help.aliyun.com/product/44282.html如果不是maven項目直接下載兩個jar導入。
以下是maven
<!-- 阿里雲短信 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.1.0</version>
</dependency>
2.2 application.yml配置阿里AK
ali:
#阿里AK 換成自己的
accessKeyId: ************************
accessKeySecret: *******************
#短信每天發送最大次數
sendMaxTime: 10
#短信失效時間 例如2分鐘= 60*1000*2 =120000
codeFailureTime: 120000
2.3 編寫阿里配置文件實體類
package com.jzyb.common.utils.wx;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "ali")
public class AliConfig {
/** accessKeyId */
private static String accessKeyId;
/** accessKeySecret */
private static String accessKeySecret;
/** 短信每天發送最大次數 */
private static Integer sendMaxTime;
/** 短信失效時間 */
private static Long codeFailureTime;
public static Integer getSendMaxTime() {
return sendMaxTime;
}
public void setSendMaxTime(Integer sendMaxTime) {
AliConfig.sendMaxTime = sendMaxTime;
}
public static Long getCodeFailureTime() {
return codeFailureTime;
}
public void setCodeFailureTime(Long codeFailureTime) {
AliConfig.codeFailureTime = codeFailureTime;
}
public static String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
AliConfig.accessKeyId = accessKeyId;
}
public static String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
AliConfig.accessKeySecret = accessKeySecret;
}
}
2.4 編寫阿里工具類
package com.jzyb.common.utils.wx;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
public class AliyunSmsUtil {
// 產品名稱:雲通信短信API產品,開發者無需替換
static final String product = "Dysmsapi";
// 產品域名,開發者無需替換
static final String domain = "dysmsapi.aliyuncs.com";
static final String accessKeyId = AliConfig.getAccessKeyId();
static final String accessKeySecret = AliConfig.getAccessKeySecret();
public static SendSmsResponse sendCode(String mobile, String code) throws ClientException {
// 可自助調整超時時間
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
// 初始化acsClient,暫不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
// 組裝請求對象-具體描述見控制檯-文檔部分內容
SendSmsRequest request = new SendSmsRequest();
// 必填:待發送手機號
request.setPhoneNumbers(mobile);
// 必填:短信簽名-可在短信控制檯中找到
// request.setSignName("蘭考縣人力資源和社會保障");
request.setSignName("鞏義人社");
// 必填:短信模板-可在短信控制檯中找到
request.setTemplateCode("SMS_154500049");
// 可選:模板中的變量替換JSON串,如模板內容爲"親愛的${name},您的驗證碼爲${code}"時,此處的值爲
request.setTemplateParam("{\"code\":\"" + code + "\"}");
// 選填-上行短信擴展碼(無特殊需求用戶請忽略此字段)
// request.setSmsUpExtendCode("90997");
// 可選:outId爲提供給業務方擴展字段,最終在短信回執消息中將此值帶回給調用者
request.setOutId("yourOutId");
// hint 此處可能會拋出異常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public static SendSmsResponse sendCode(String signName, String templateCode, String mobile, String code)
throws ClientException {
// 可自助調整超時時間
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
// 初始化acsClient,暫不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
// 組裝請求對象-具體描述見控制檯-文檔部分內容
SendSmsRequest request = new SendSmsRequest();
// 必填:待發送手機號
request.setPhoneNumbers(mobile);
// 必填:短信簽名-可在短信控制檯中找到
request.setSignName(signName);
// 必填:短信模板-可在短信控制檯中找到
request.setTemplateCode(templateCode);
// 可選:模板中的變量替換JSON串,如模板內容爲"親愛的${name},您的驗證碼爲${code}"時,此處的值爲
request.setTemplateParam("{\"code\":\"" + code + "\"}");
// 選填-上行短信擴展碼(無特殊需求用戶請忽略此字段)
// request.setSmsUpExtendCode("90997");
// 可選:outId爲提供給業務方擴展字段,最終在短信回執消息中將此值帶回給調用者
request.setOutId("yourOutId");
// hint 此處可能會拋出異常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public static int getCode() {
return (int) ((Math.random() * 9 + 1) * 100000);
}
public static void main(String[] args) {
try {
sendCode("模板名", "SMS_*********", "手機號", getCode() + "");
} catch (ClientException e) {
e.printStackTrace();
}
}
}
2.5 編寫短信緩存
package com.jzyb.common.utils.wx;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
public class Cache {
// 鍵值對集合
private final static Map<String, Entity> map = new HashMap<>();
// 定時器線程池,用於清除過期緩存
private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
/**
* 添加緩存
*
* @param key
* 鍵
* @param data
* 值
*/
public synchronized static void put(String key, Object data) {
Cache.put(key, data, 0);
}
/**
* 添加緩存
*
* @param key
* 鍵
* @param data
* 值
* @param expire
* 過期時間,單位:毫秒, 0表示無限長
*/
public synchronized static void put(final String key, Object data, long expire) {
// 清除原鍵值對
Cache.remove(key);
// 設置過期時間
if (expire > 0) {
Future future = executor.schedule(new Runnable() {
@Override
public void run() {
// 過期後清除該鍵值對
synchronized (Cache.class) {
map.remove(key);
}
}
}, expire, TimeUnit.MILLISECONDS);
map.put(key, new Entity(data, future));
} else {
// 不設置過期時間
map.put(key, new Entity(data, null));
}
}
/**
* 讀取緩存
*
* @param key
* 鍵
* @return
*/
public synchronized static Object get(String key) {
Entity entity = map.get(key);
return entity == null ? null : entity.getValue();
}
/**
* 讀取緩存
*
* @param key
* 鍵 * @param clazz 值類型
* @return
*/
public synchronized static <T> T get(String key, Class<T> clazz) {
return clazz.cast(Cache.get(key));
}
/**
* 清除緩存
*
* @param key
* @return
*/
public synchronized static Object remove(String key) {
// 清除原緩存數據
Entity entity = map.remove(key);
if (entity == null)
return null;
// 清除原鍵值對定時器
Future future = entity.getFuture();
if (future != null)
future.cancel(true);
return entity.getValue();
}
/**
* 查詢當前緩存的鍵值對數量
*
* @return
*/
public synchronized static int size() {
return map.size();
}
/**
* 緩存實體類
*/
private static class Entity {
// 鍵值對的value
private Object value;
// 定時器Future
private Future future;
public Entity(Object value, Future future) {
this.value = value;
this.future = future;
}
/**
* 獲取值
*
* @return
*/
public Object getValue() {
return value;
}
/**
* 獲取Future對象
*
* @return
*/
public Future getFuture() {
return future;
}
}
}
2.6 編寫發送短信隨機碼–6位數
public static int getCode(){
return (int)((Math.random()*9+1)*100000);
}
2.7 發送短信接口
@ApiOperation(value = "發送短信驗證碼", notes = "通過手機號發送短信,參數:手機號")
@ApiImplicitParams({
@ApiImplicitParam(name = "mobile", value = "15858585858", required = true, paramType = "form", dataType = "String"), })
@GetMapping(value = "sendCode", produces = { "application/json;charset=UTF-8" })
public AjaxResult sendCode(String mobile) {
try {
String code = getCode()+"";
SendSmsResponse sr=AliyunSmsUtil.sendCode("模板名", "模板id",mobile, code);
if("OK".equals(sr.getCode())) {
Cache.put(mobile.trim(), code, 120000);
String code11 = (String) Cache.get(mobile);
return AjaxResult.success("發送成功");
}
} catch (ClientException e) {
e.printStackTrace();
}
return AjaxResult.error("操作失敗");
}
2.8 校驗驗證碼有沒有過期
String oldCode = (String) Cache.get(mobile);
if(StringUtils.isNotEmpty(oldCode)) {
return AjaxResult.warn("之前驗證碼還沒失效");
}
2.9 判斷手機號是否爲空,手機格式是否有誤
if(!isPhone(mobile)) {
return AjaxResult.warn("手機號格式有誤");
}
public static boolean isPhone(String phone) {
String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
if (phone.length() != 11) {
//MToast.showToast("手機號應爲11位數");
return false;
} else {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(phone);
boolean isMatch = m.matches();
//LogUtil.e(isMatch);
if (!isMatch) {
// MToast.showToast("請填入正確的手機號");
}
return isMatch;
}
}
2.10 驗證成功後,移除緩存中的手機號驗證碼
Cache.remove(webUser.getMobile());
如果沒啥意外,即可發送成功!
{
"msg": "發送成功",
"code": 0
}
3 修改短信接口
3.1 添加短信類型
@ApiOperation(value = "發送短信驗證碼", notes = "通過手機號發送短信,參數:手機號,type類別type 1.註冊 2.修改密碼 3.修改手機號 4 修改個人信息 5 忘記密碼")
@ApiImplicitParams({
@ApiImplicitParam(name = "mobile", value = "15858585858", required = true, paramType = "form", dataType = "String"),
@ApiImplicitParam(name = "type", value = "2", required = true, paramType = "form", dataType = "String"),})
@GetMapping(value = "sendCode")
public AjaxResult sendCode(String mobile,String type) {
if(StringUtils.isNotBlank(mobile)&&StringUtils.isNotBlank(type)){
//類別不爲空的情況
}else{
//返回參數爲空
}
3.2 獲取配置文件中短信時間跟最大次數
if(webSmsLogService.countByMobile(mobile)>AliConfig.getSendMaxTime()) {
return AjaxResult.warn("當天接收驗證碼超限制");
}
3.3 對特別類型下進行校驗
/*
* 1.註冊:拿註冊舉例,如果用戶表已經有了改用戶,就沒有必要給這個人發送短信,因爲已經註冊過
* 2.忘記密碼:如果用戶表根本就沒有這個手機號,就沒有必要發忘記密碼的短信--可以根據自己短信種類做判斷。規避短信濫用
*/
if("1".equals(type)) {
try {
//判斷該手機號是否已經存在--存在表示用戶已經註冊過,不存在的情況才能發註冊的驗證碼
if(webUserService.checkMobileSole(mobile)) {
return AjaxResult.warn("手機號已經存在");
}
String oldCode = (String) Cache.get(mobile);
if(StringUtils.isNotEmpty(oldCode)) {
return AjaxResult.warn("之前驗證碼還沒失效");
}
String code = getCode()+"";
SendSmsResponse sr=AliyunSmsUtil.sendCode("模板名", "模板id",mobile, code);
if("OK".equals(sr.getCode())) {
if("OK".equals(sr.getCode())) {
//發送成功將手機號 驗證碼 失效時間保存到Cache中
Cache.put(mobile.trim(), code, AliConfig.getCodeFailureTime());
return AjaxResult.success("發送成功");
}
} catch (ClientException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return AjaxResult.error("調用阿里短信驗證碼失敗");
}
}
3.4 動態設置失效時間
Cache.put(mobile.trim(), code, AliConfig.getCodeFailureTime());
3.5 規定時間內沒有失效的校驗
String oldCode = (String) Cache.get(mobile);
if(StringUtils.isNotEmpty(oldCode)) {
return AjaxResult.warn("之前驗證碼還沒失效");
}
3.6新建表,保存短信日誌,
CREATE TABLE `sys_sms_log` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`phone` varchar(11) DEFAULT NULL COMMENT '手機號',
`send_time` datetime DEFAULT NULL COMMENT '發送時間',
`send_status` char(1) DEFAULT NULL COMMENT '發送狀態:0失敗;1成功',
`content` varchar(100) DEFAULT NULL COMMENT '發送內容',
`send_type` char(1) DEFAULT NULL COMMENT '調用方式:1.註冊 2.修改密碼 3.修改手機號 4 修改個人信息 5 忘記密碼.',
`result_code` varchar(50) DEFAULT NULL COMMENT '短信運營商的返回碼',
`send_bizId` varchar(50) DEFAULT NULL COMMENT '短信發送回執ID',
`send_ip` varchar(20) DEFAULT NULL COMMENT '發送短信的IP地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='短信發送日誌'
每次發送成功後將信息保存短信日誌表
SendSmsResponse sr=AliyunSmsUtil.sendCode("模板名", "模板id",mobile, code);
if("OK".equals(sr.getCode())) {
//發送成功將手機號 驗證碼 失效時間保存到Cache中
Cache.put(mobile.trim(), code, AliConfig.getCodeFailureTime());
String code11 = (String) Cache.get(mobile);
WebSmsLog smslog = new WebSmsLog();
smslog.setContent("");
smslog.setPhone(mobile);
smslog.setResultCode("OK");
smslog.setSendBizid(sr.getRequestId());
//smslog.setSendIp(sendIp);
smslog.setSendStatus(1);
smslog.setSendTime(DateUtils.getNowDate());
smslog.setSendType(type);
sysSmsLogService.insertSysSmsLog(smslog);
return AjaxResult.success("發送成功");
}
3.8 結果圖
發送結果
4 注意點
4.1 模板簽名正確性
AK --模板名--模板id這些都是對應的,項目多了有時候會把另外一個項目的AK拿到這個項目用,但是模板id卻是這個項目的,儘量多審查幾遍,保證模板設置要與控制檯的短信模板相對應
4.2 授權問題
(遇到過一次)需要阿里後臺點一下
4.3 儘量把次數,失效時間,AK寫到配置文件,方便修改
4.4 謝謝點贊 ^o^
本人小白,以上是自己的一知半解,有任何問題請留言評論,及時改正,及時回覆,互相學習互相進步。手打2小時,也希望對新手有幫助,看的開心話點贊關注。原創–轉載請備明出處–叩謝!