总结:

  • 配置好阿里云短信服务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

本站以现代、古代情诗为主,情诗网创办于2013年,以原创爱情诗歌、经典情诗、现代情诗、古代情诗、英文情诗、情诗绝句为主并收集古诗、古诗词、诗歌大全、诗词名句的文学门户。方便您下次继续阅读;可以放在浏览器的收藏夹中(快捷键Ctrl+D);或者看到喜欢或者有趣的诗词可以通过分享按钮给你的好友分享;情诗网是目前最全情诗大全网站之一。并欢迎广大诗歌爱好者阅览投稿!喜欢本站的话请大家把本站告诉给你朋友哦!地址是 www.qingshiwang.com !