平台短信服务接入

平台官网地址:

        华为云阿里云腾讯云七牛云

因各个平台规则不一样,需查看相应官网文档。文章主要封装简单功能,复杂功能,需要查看文档。

1.pom

          <groupId>com.aliyun</groupId>
            <artifactId>dysmsapi20170525</artifactId>
            <version>2.0.18</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java</artifactId>
            <version>3.1.574</version>
        </dependency>
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>7.11.0</version>
        </dependency>

2.策略模式

import com.lean.sms.properties.SmsProperties;

import java.util.Map;

/**
 * 短信
 *
 * @author 
 */
public abstract class SmsStrategy {

    public  SmsProperties smsProperties;

    /**
     * 发送短信
     * @param mobile  手机号
     * @param params  参数
     */
   public abstract void send(String mobile, Map<String, String> params);
}

华为云:

package com.lean.sms.strategy;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import com.lean.sms.properties.SmsProperties;
import com.lean.sms.utils.JsonUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;

import javax.net.ssl.*;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 华为云短信
 *  https://support.huaweicloud.com/productdesc-msgsms/sms_templates.html
 * https://support.huaweicloud.com/devg-msgsms/sms_04_0002.html#ZH-CN_TOPIC_0000001174512727__section1551232617020
 * @author 
 */
public class HuaweiSmsStrategy extends SmsStrategy {

    // 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";

    // 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";

    public HuaweiSmsStrategy(SmsProperties smsProperties) {
        this.smsProperties = smsProperties;
    }

    @Override
    public void send(String mobile, Map<String, String> params) {
        // 有参数则设置
        String templateParas = null;
        if (MapUtil.isNotEmpty(params)) {
            templateParas = JsonUtils.toJsonString(params.values().toArray(new String[0]));
        }

        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
        String receiver = "+86" + mobile; //短信接收人号码

        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
        String statusCallBack = "";

        // 请求Body,不携带签名名称时,signature请填null
        String body = buildRequestBody(smsProperties.getHuawei().getSenderId(), receiver, smsProperties.getHuawei().getTemplateId(), templateParas, statusCallBack, smsProperties.getHuawei().getSignName());
        if (StringUtils.isBlank(body)) {
            throw new RuntimeException("body is null.");
        }

        // 请求Headers中的X-WSSE参数值
        String wsseHeader = buildWsseHeader(smsProperties.getConfig().getAccessKey(), smsProperties.getConfig().getSecretKey());
        if (StringUtils.isBlank(wsseHeader)) {
            throw new RuntimeException("wsse header is null.");
        }

        try {
            // 使用 https
            trustAllHttpsCertificates();

            URL realUrl = new URL(smsProperties.getHuawei().getUrl());
            HttpsURLConnection connection = (HttpsURLConnection) realUrl.openConnection();
            HostnameVerifier hv = (hostname, session) -> true;
            connection.setHostnameVerifier(hv);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
            connection.setRequestProperty("X-WSSE", wsseHeader);
            connection.connect();

            IoUtil.writeUtf8(connection.getOutputStream(), true, body);

            int status = connection.getResponseCode();
            if (status == HttpStatus.OK.value()) {
                String response = IoUtil.read(connection.getInputStream(), CharsetUtil.CHARSET_UTF_8);
                HuaweiSmsResult result = JsonUtils.parseObject(response, HuaweiSmsResult.class);

                // 短信是否发送成功
                assert result != null;
                if (!"000000".equals(result.code)) {
                    throw new RuntimeException(result.description);
                }
            } else { //400 401
                throw new RuntimeException(IoUtil.read(connection.getErrorStream(), CharsetUtil.CHARSET_UTF_8));
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 构造请求Body体
     *
     * @param signature | 签名名称,使用国内短信通用模板时填写
     */
    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String statusCallBack, String signature) {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
            throw new RuntimeException("buildRequestBody(): sender, receiver or templateId is null.");
        }
        Map<String, String> map = new HashMap<>();

        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && !templateParas.isEmpty()) {
            map.put("templateParas", templateParas);
        }
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            map.put("statusCallback", statusCallBack);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }

        StringBuilder sb = new StringBuilder();
        String temp = "";

        for (String s : map.keySet()) {
            try {
                temp = URLEncoder.encode(map.get(s), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sb.append(s).append("=").append(temp).append("&");
        }

        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    /**
     * 构造X-WSSE参数值
     */
    static String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            throw new RuntimeException("buildWsseHeader(): appKey or appSecret is null.");
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date());
        String nonce = UUID.randomUUID().toString().replace("-", "");

        MessageDigest md;
        byte[] passwordDigest = null;

        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);

    }

    static void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {

                    }

                    public void checkServerTrusted(X509Certificate[] chain, String authType) {

                    }

                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                }
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    @Data
    static class HuaweiSmsResult {
        // code为000000,表示成功
        private String code;
        private String description;
        private List<Object> result;
    }
}

阿里云:

package com.lean.sms.strategy;

import cn.hutool.core.map.MapUtil;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.lean.sms.properties.SmsProperties;
import com.lean.sms.utils.Constant;
import com.lean.sms.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

/**
 * 阿里云短信
 *https://help.aliyun.com/document_detail/71160.htm?spm=a2c4g.11186623.0.0.51d99373cLZqvl
 * @author 
 */
@Slf4j
public class AliyunSmsStrategy extends SmsStrategy {

    private final Client client;

    public AliyunSmsStrategy(SmsProperties smsProperties) {
        this.smsProperties = smsProperties;

        try {
            Config config = new Config();
            // 您的AccessKey ID
            config.setAccessKeyId(smsProperties.getConfig().getAccessKey());
            // 您的AccessKey Secret
            config.setAccessKeySecret(smsProperties.getConfig().getSecretKey());
            config.endpoint = "dysmsapi.aliyuncs.com";

            this.client = new Client(config);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void send(String mobile, Map<String, String> params) {
        SendSmsRequest request = new SendSmsRequest();
        request.setSignName(smsProperties.getAliyun().getSignName());
        request.setTemplateCode(smsProperties.getAliyun().getTemplateId());
        request.setPhoneNumbers(mobile);
        //设置模板参数
        if (MapUtil.isNotEmpty(params)) {
            request.setTemplateParam(JsonUtils.toJsonString(params));
        }
        try {
            // 发送短信
            SendSmsResponse response = client.sendSms(request);
            // 发送失败
            if (!Constant.OK.equalsIgnoreCase(response.getBody().getCode())) {
                throw new RuntimeException(response.getBody().getMessage());
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}

七牛云:

import com.lean.sms.properties.SmsProperties;
import com.qiniu.http.Response;
import com.qiniu.sms.SmsManager;
import com.qiniu.util.Auth;
import java.util.Map;

/**
 * 七牛云短信
 *https://developer.qiniu.com/sms/5993/sms-sdk
 * @author 
 */
public class QiniuSmsStrategy extends SmsStrategy {

    private final SmsManager smsManager;

    public QiniuSmsStrategy(SmsProperties smsProperties) {
        this.smsProperties = smsProperties;
        Auth auth = Auth.create(smsProperties.getConfig().getAccessKey(), smsProperties.getConfig().getSecretKey());
        smsManager = new SmsManager(auth);
    }

    @Override
    public void send(String mobile, Map<String, String> params) {
        try {
            Response response = smsManager.sendSingleMessage(smsProperties.getQiniu().getTemplateId(), mobile, params);
            // 是否发送成功
            if (!response.isOK()) {
                throw new RuntimeException(response.error);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}

腾讯云:

package com.lean.sms.strategy;

import cn.hutool.core.map.MapUtil;
import com.lean.sms.properties.SmsProperties;
import com.lean.sms.utils.Constant;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;

import java.util.Map;

/**
 * 腾讯云短信
 *https://cloud.tencent.com/document/api/382/52077
 * @author 
 */
public class TencentSmsStrategy extends SmsStrategy {

    private SmsClient client;

    public TencentSmsStrategy(SmsProperties smsProperties) {
        this.smsProperties = smsProperties;

        try {
            // 实例化一个http选项,可选的
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setReqMethod("POST");
            httpProfile.setConnTimeout(30); // 请求连接超时时间,单位为秒(默认60秒)
            httpProfile.setWriteTimeout(30);  // 设置写入超时时间,单位为秒(默认0秒)
            httpProfile.setReadTimeout(30);  // 设置读取超时时间,单位为秒(默认0秒)
            httpProfile.setEndpoint("sms.tencentcloudapi.com"); // 指定接入地域域名(默认就近接入)
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setSignMethod("HmacSHA256"); // 指定签名算法(默认为HmacSHA256)
            // 自3.1.80版本开始,SDK 支持打印日志。
            clientProfile.setHttpProfile(httpProfile);
            Credential cred = new Credential(smsProperties.getConfig().getAccessKey(), smsProperties.getConfig().getSecretKey());
            this.client = new SmsClient(cred, "ap-shanghai", clientProfile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void send(String mobile, Map<String, String> params) {
        SendSmsRequest request = new SendSmsRequest();
        request.setSmsSdkAppId(smsProperties.getTencent().getAppId());
        request.setSignName(smsProperties.getTencent().getSignName());
        request.setTemplateId(smsProperties.getTencent().getTemplateId());
        // 有参数则设置
        if (MapUtil.isNotEmpty(params)) {
            request.setTemplateParamSet(params.values().toArray(new String[0]));
        }
        //采用 E.164 标准,格式为+[国家或地区码][手机号],单次请求最多支持200个手机号且要求全为境内手机号或全为境外手机号。
        // 区段 + 手机号
        String[] phoneNumberSet = {"+86" + mobile};
        request.setPhoneNumberSet(phoneNumberSet);
        // 国际、港澳台短信,需要添加SenderId,国内短信填空,默认未开通
        request.setSenderId(smsProperties.getTencent().getSenderId());
        try {
            // 发送短信
            SendSmsResponse response = client.SendSms(request);
            SendStatus sendStatus = response.getSendStatusSet()[0];
            // 发送失败
            if (!Constant.OK.equalsIgnoreCase(sendStatus.getCode())) {
                throw new RuntimeException(sendStatus.getMessage());
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}

3.配置

import com.lean.sms.enums.SmsPlatformEnum;
import com.lean.sms.properties.SmsProperties;
import com.lean.sms.strategy.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 短信 Context
 *
 * @author 
 */
@Configuration
@EnableConfigurationProperties(SmsProperties.class)
@ConditionalOnProperty(prefix = "sms", value = "enabled")
public class SmsConfiguration {

    @Bean
    public SmsStrategy smsStrategy(SmsProperties smsProperties) {
        if (smsProperties.getConfig().getType() == SmsPlatformEnum.ALIYUN) {
            return new AliyunSmsStrategy(smsProperties);
        } else if (smsProperties.getConfig().getType() == SmsPlatformEnum.TENCENT) {
            return new TencentSmsStrategy(smsProperties);
        } else if (smsProperties.getConfig().getType() == SmsPlatformEnum.QINIU) {
            return new QiniuSmsStrategy(smsProperties);
        } else if (smsProperties.getConfig().getType() == SmsPlatformEnum.HUAWEI) {
            return new HuaweiSmsStrategy(smsProperties);
        } else {
            throw new RuntimeException("未知的短信平台");
        }
    }
}


/**
 * 短信平台枚举
 */
@Getter
@AllArgsConstructor
public enum SmsPlatformEnum {
    /**
     * 阿里云
     */
    ALIYUN,
    /**
     * 腾讯云
     */
    TENCENT,
    /**
     * 七牛云
     */
    QINIU,
    /**
     * 华为云
     */
    HUAWEI;
}

代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值