阿里雲簡訊服務 傳送簡訊驗證碼 區分業務場景
摘要:
總結:
配置好阿里雲簡訊服務API呼叫所需配置項資訊
按業務場景自定義標識碼,做到正確傳送不同場景的簡訊驗證碼
有一點需要特別注意:寫這邊文章時,偶然發現阿里雲簡訊服務API,<u> 在遇到以數字0開頭的隨機數碼時,傳送的驗證碼會忽略數字0,導致驗證碼長度...
總結:
- 配置好阿里雲簡訊服務API呼叫所需配置項資訊
- 按業務場景自定義標識碼,做到正確傳送不同場景的簡訊驗證碼
- 有一點需要特別注意:寫這邊文章時,偶然發現阿里雲簡訊服務API,<u> 在遇到以數字0開頭的隨機數碼時,傳送的驗證碼會忽略數字0,導致驗證碼長度不匹配 </u>。因此,建議在生成隨機數碼時,使用遞迴思想提前將 以數字0開頭的隨機數碼過濾掉!
/** * @Description 阿里雲簡訊服務 控制層 * @Author blake * @Date 2018/12/6 下午5:06 * @Version 1.0 */ @Api(tags = "05\. 阿里雲簡訊服務", description = "阿里雲簡訊驗證碼") @RestController @RequestMapping("/api/common/sms") public class AliyunSmsController extends BaseController { @Autowired private AliyunSmsService aliyunSmsService; /** * 1)簡訊防刷 藉助Redis * 2)非同步接收簡訊傳送狀態,更新本地資料庫的簡訊傳送 成功與否標識位 */ @ApiOperation(value = "傳送簡訊驗證碼", response = Boolean.class) @PostMapping("/verification/code") public Response sendSms(@RequestBody @Valid SmsVerifyCodeRequest request) throws ClientException { return JsonSend.success(aliyunSmsService.sendSms(request)); } } /** * @Description 阿里雲簡訊服務 業務邏輯層 * @Author blake * @Date 2018/12/6 下午5:24 * @Version 1.0 */ @Service public class AliyunSmsServiceImpl implements AliyunSmsService { private static final Logger logger = LoggerFactory.getLogger(AliyunSmsServiceImpl.class); @Autowired private AliyunSmsProperties aliyunSmsProperties; @Autowired private BeePlusUserAPIs beePlusUserAPIs; @Autowired private RedissonClient redissonClient; @Autowired private SmsSendLogDAO smsSendLogDAO; /** * @return java.lang.Boolean * @throws * @description 傳送簡訊驗證碼 * @params [request] */ @Override public Boolean sendSms(SmsVerifyCodeRequest request) throws ClientException { String phone = request.getPhone(); Boolean isCheckBusiness = request.getIsCheckBusiness(); if (Objects.nonNull(isCheckBusiness) && isCheckBusiness) { // 呼叫Bee+方API,判斷是否為會員身份,若不是會員,則提示"手機號碼不存在,請移步前臺辦理會員!";若為會員, // 業務邏輯繼續傳送驗證碼,完成會員資訊繫結 // BeePlus方會員身份校驗API String toDetectMemberApi = beePlusUserAPIs.getToDetectMemberApi(); if (StringUtils.isBlank(toDetectMemberApi)) { throw new CommonBusinessException("toDetectMemberApi配置有誤,請核查!"); } MemberDetectReqDTO memberDetectReqDTO = new MemberDetectReqDTO(); memberDetectReqDTO.setPhone(phone); String jsonPhone = JacksonUtil.toJSon(memberDetectReqDTO); // 校驗手機號碼為phone的會員是否存在 String exists = HttpUtils.doPost(toDetectMemberApi, Objects.requireNonNull(jsonPhone)); if (IntegerUtil.toInt(exists) != 1) { throw new CommonBusinessException("手機號碼不存在,請移步前臺辦理會員!"); } } // 從Redis取出使用者傳送簡訊驗證碼的間隔時效標識 RBucket<Object> bucketDuration = redissonClient.getBucket(Constants.COUNT_DOWN_SEND_SMS_PREFIX + phone); if (bucketDuration.isExists()) { logger.info("AliyunSmsServiceImpl.sendSms ========== 簡訊驗證碼傳送頻繁,限定每{}分鐘傳送一次 ========== ", Constants.VERIFY_CODE_SEND_DURATION); throw new CommonBusinessException("簡訊驗證碼傳送頻繁,請稍候再試!"); } Integer codeType = request.getCodeType(); // 可自助調整超時時間 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); String accessKeyId = aliyunSmsProperties.getAccessKeyId(); String accessKeySecret = aliyunSmsProperties.getAccessKeySecret(); String product = aliyunSmsProperties.getProduct(); String domain = aliyunSmsProperties.getDomain(); // 簡訊簽名:形如【蜜蜂科技】 String smsSign = Constants.SMS_SING; // 初始化acsClient,暫不支援region化 IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); // 組裝請求物件-具體描述見控制檯-文件部分內容 SendSmsRequest smsRequest = new SendSmsRequest(); // 必填:待發送手機號 smsRequest.setPhoneNumbers(phone); // 必填:簡訊簽名-可在簡訊控制檯中找到 smsRequest.setSignName(smsSign); // 6位長度隨機碼 String randomCode = RandomUtils.getRandomCode(); // 簡訊除錯開關,萬能簡訊驗證碼 = "1234" if (aliyunSmsProperties.getSimulate()) { randomCode = "1234"; } // 使用redis儲存,藉助其key可設定過期時間 // 無論key是否存在,key=value的鍵始終存在 RBucket<Object> randomCodeBucket = redissonClient.getBucket(Constants.VERIFY_CODE_SESSION_PREFIX + phone); // 設定value的同時配置key存活時間 randomCodeBucket.set(randomCode, Constants.VERIFY_CODE_VALID_SECONDS, TimeUnit.SECONDS); // 設定簡訊驗證碼傳送間隔 bucketDuration.set(1, Constants.VERIFY_CODE_SEND_DURATION, TimeUnit.MINUTES); // 簡訊除錯開關 if (aliyunSmsProperties.getSimulate()) { return true; } // 匹配訊息模板 chooseSmsTemplate(codeType, smsRequest, randomCode); // 選填-上行簡訊擴充套件碼(無特殊需求使用者請忽略此欄位) // smsRequest.setSmsUpExtendCode("90997"); // 可選:outId為提供給業務方擴充套件欄位,最終在簡訊回執訊息中將此值帶回給呼叫者 // smsRequest.setOutId("yourOutId"); // hint 此處可能會丟擲異常,注意catch SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(smsRequest); logger.info("AliyunSmsServiceImpl.sendSms =========== sendSmsResponse:{} ===========", JacksonUtil.toJSon(sendSmsResponse)); // 簡訊內容 String msgContent = ""; //查明細 if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) { QuerySendDetailsResponse querySendDetailsResponse = querySendDetails(sendSmsResponse.getBizId(), phone); List<QuerySendDetailsResponse.SmsSendDetailDTO> smsSendDetailDTOs = querySendDetailsResponse.getSmsSendDetailDTOs(); if (CollectionUtil.isNotEmpty(smsSendDetailDTOs)) { logger.debug("AliyunSmsServiceImpl.sendSms =========== 簡訊明細查詢介面返回資料 ==========="); logger.debug("AliyunSmsServiceImpl.sendSms =========== Code:[{}] ===========", querySendDetailsResponse.getCode()); logger.debug("AliyunSmsServiceImpl.sendSms =========== Message:[{}] ===========", querySendDetailsResponse.getMessage()); for (QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO : smsSendDetailDTOs) { logger.info("AliyunSmsServiceImpl.sendSms =========== smsSendDetailDTO:{} ===========", JacksonUtil.toJSon(smsSendDetailDTO)); msgContent = smsSendDetailDTO.getContent(); } } // 儲存簡訊驗證碼傳送記錄 smsSendLogDAO.insertSmsSendLog(phone, codeType, randomCode, msgContent, true); return true; } else { smsSendLogDAO.insertSmsSendLog(phone, codeType, randomCode, msgContent, false); return false; } } /** * @return com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse * @throws * @description 查詢簡訊傳送記錄 * @params [bizId, phone] */ private QuerySendDetailsResponse querySendDetails(String bizId, String phone) throws ClientException { // 可自助調整超時時間 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); String accessKeyId = aliyunSmsProperties.getAccessKeyId(); String accessKeySecret = aliyunSmsProperties.getAccessKeySecret(); String product = aliyunSmsProperties.getProduct(); String domain = aliyunSmsProperties.getDomain(); //初始化acsClient,暫不支援region化 IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //組裝請求物件 QuerySendDetailsRequest request = new QuerySendDetailsRequest(); //必填-號碼 request.setPhoneNumber(phone); //可選-流水號 request.setBizId(bizId); //必填-傳送日期 支援30天內記錄查詢,格式yyyyMMdd SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd"); request.setSendDate(ft.format(new Date())); //必填-頁大小 request.setPageSize(10L); //必填-當前頁碼從1開始計數 request.setCurrentPage(1L); //hint 此處可能會丟擲異常,注意catch QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request); return querySendDetailsResponse; } /** * @return void * @throws * @description 根據簡訊驗證碼型別,匹配訊息模板 * @params [codeType, smsRequest, randomCode] */ private void chooseSmsTemplate(Integer codeType, SendSmsRequest smsRequest, String randomCode) { switch (codeType) { case Constants.VerifyCodeType.VERIFY_IDENTITY_CODE: //必填:簡訊模板-可在簡訊控制檯中找到 smsRequest.setTemplateCode("SMS_152380369"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; case Constants.VerifyCodeType.LOGIN_CONFIRM_CODE: smsRequest.setTemplateCode("SMS_152380368"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; case Constants.VerifyCodeType.LOGIN_UN_NORMAL_CODE: smsRequest.setTemplateCode("SMS_152380367"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; case Constants.VerifyCodeType.USER_REGISTER_CODE: smsRequest.setTemplateCode("SMS_152380366"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; case Constants.VerifyCodeType.MODIFY_PASSWORD_CODE: smsRequest.setTemplateCode("SMS_152380365"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; case Constants.VerifyCodeType.INFORMATION_CHANGE_CODE: smsRequest.setTemplateCode("SMS_152380364"); smsRequest.setTemplateParam("{\"code\":" + randomCode + "}"); break; } } } public class Constants { /** * 簡訊驗證型別標識碼 */ public interface VerifyCodeType { // 驗證型別1=> 身份驗證 int VERIFY_IDENTITY_CODE = 1; // 驗證型別2=> 登入確認 int LOGIN_CONFIRM_CODE = 2; // 驗證型別3=> 登入異常 int LOGIN_UN_NORMAL_CODE = 3; // 驗證型別4=> 使用者註冊 int USER_REGISTER_CODE = 4; // 驗證型別5=> 修改密碼 int MODIFY_PASSWORD_CODE = 5; // 驗證型別6=> 資訊變更 int INFORMATION_CHANGE_CODE = 6; } }
image.gif