阿里云短信发送接口直接HTTP请求调用

最新一个个性化项目,客户要求使用阿里云短信发送接口。

但是实在不想引入阿里云的SDK,就自己生成签名,拼接URL实现,  其实用他们的SDK最终也是一哥GET请求进行调用,归根接底还是HTTP请求。

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.net.UnknownServiceException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import sun.misc.BASE64Encoder;

import com.commnetsoft.PropertyEnum;
import com.commnetsoft.BaseErrorCode.Base;
import com.commnetsoft.admin.model.sys.Messagequeue;
import com.commnetsoft.admin.model.sys.MsgApi;
import com.commnetsoft.admin.service.sys.MsgApiService;
import com.commnetsoft.core.SpringContext;
import com.commnetsoft.spi.IErrorCode;
import com.commnetsoft.spi.exception.ServiceUnavailableException;
import com.commnetsoft.util.HttpUtil;
import com.commnetsoft.util.SerializerUtil;

/**
 * 阿里云短信发送实现
 * @author wangyijie
 * @date 2018年1月10日
 * @version 1.0
 */
@Service
public class AliyunSendMsg extends AbstractSendSMS {
	
	private final static Logger logger = LoggerFactory.getLogger(AliyunSendMsg.class);
	
	private MsgApiService msgApiService = SpringContext.getBean("msgApiService");

	@Override
	public String getName() {
		return "阿里云短信发送";
	}

	@Override
	@SuppressWarnings("unchecked")
	public IErrorCode doSend(Messagequeue msg) throws ServiceUnavailableException,
			MalformedURLException, UnknownServiceException, IOException {
		
		String accessKeyId = PropertyEnum.ALIYUN_SENDMSG_ACCESSKEYID.getValue();
		if (StringUtils.isBlank(accessKeyId)) {
			logger.error("阿里云短信发送未配置,需要在系统设置——阿里云短信发送设置中填入accessKeyId");
			return Base.FAIL;
		}
		String accessSecret = PropertyEnum.ALIYUN_SENDMSG_ACCESSSECRET.getValue();
		if (StringUtils.isBlank(accessSecret)) {
			logger.error("阿里云短信发送未配置,需要在系统设置——阿里云短信发送设置中填入accessSecret");
			return Base.FAIL;
		}
		
		String param = msg.getContent();
		//现将内容序列化成map
		Map paramMap = SerializerUtil.deserializeObj(param, Map.class);
		if (paramMap == null || paramMap.size() < 1) {
			logger.error("阿里云短信发送失败——不合法的参数!参数={}", param);
			return Base.FAIL;
		}
		//获取设置的短信签名
		String signName = paramMap.get("signName");
		if (StringUtils.isBlank(signName)) {
			logger.error("阿里云短信发送失败——参数中未设置signName!参数={}", param);
			return Base.FAIL;
		}
		//获取设置的短信模板ID
		String templateCode = paramMap.get("templateCode");
		if (StringUtils.isBlank(templateCode)) {
			logger.error("阿里云短信发送失败——参数中未设置templateCode!参数={}", param);
			return Base.FAIL;
		}
		//获取要发送的内容
		String content = paramMap.get("content");
		if (StringUtils.isBlank(content)) {
			logger.error("阿里云短信发送失败——参数中未设置要发送的内容!参数={}", param);
			return Base.FAIL;
		}
		//目标手机号
		String phone = msg.getTargetPhone();
		
		/**
		 * 设置参数
		 */
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 
		//设置时区
		df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
		Map paras = new HashMap();
		
		//固定参数
        paras.put("SignatureMethod", "HMAC-SHA1");
        //用于请求的防重攻击,每次请求唯一,用:java.util.UUID.randomUUID()生成
        paras.put("SignatureNonce", UUID.randomUUID().toString());
        //阿里云分配的用户唯一ID
        paras.put("AccessKeyId", accessKeyId);
        //固定值:1.0
        paras.put("SignatureVersion", "1.0");
        //格式化后的当前时间(时间与阿里云服务器时间相差15分钟会被拒绝请求)
        paras.put("Timestamp", df.format(new Date()));
        //返回参数格式(可选JSON/XML)
        paras.put("Format", "JSON");
		
        //API的命名,固定值
        paras.put("Action", "SendSms");
        //API的版本,固定值
        paras.put("Version", "2017-05-25");
        //API支持的RegionID
        paras.put("RegionId", "cn-hangzhou");
        //短信接收号码,支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
        paras.put("PhoneNumbers", phone);
        //短信签名(用户账号可新增签名,需审批)
        paras.put("SignName", signName);
        //短信模板变量替换JSON串,如果JSON中需要带换行符,参照标准的JSON协议。  如:{“code”:”1234”,”product”:”ytx”}    {\"customer\":\"nihao\"}
        paras.put("TemplateParam", content);	
        //短信模板ID(可自定义,但是需要审批)
        paras.put("TemplateCode", templateCode);
        
        //获取阿里云发送短信的服务器地址
        Integer msgApiId = getMsgapiid();
        MsgApi msgApi = msgApiService.queryById(msgApiId);
        String aliyunUrl = msgApi.getHttpurl();
        
        //参数KEY排序
        TreeMap sortParas = new TreeMap();
        sortParas.putAll(paras);
        
        try {
        	//构造待签名的字符串
        	Iterator it = sortParas.keySet().iterator();
        	StringBuilder sortQueryStringTmp = new StringBuilder();
        	while (it.hasNext()) {
        		String key = it.next();
        		sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
        	}
        	// 去除第一个多余的&符号
        	String sortedQueryString = sortQueryStringTmp.substring(1);
        	StringBuilder stringToSign = new StringBuilder();
        	stringToSign.append("GET").append("&");
        	stringToSign.append(specialUrlEncode("/")).append("&");
        	stringToSign.append(specialUrlEncode(sortedQueryString));
        	String sign = sign(accessSecret + "&", stringToSign.toString());
        	
        	//签名最后也要做特殊URL编码
        	String signature = specialUrlEncode(sign);
        	
        	//生成最后的请求url
        	String url = aliyunUrl + "?Signature=" + signature + sortQueryStringTmp;
        	String result = HttpUtil.sendGet(url);
        	
        	if (StringUtils.isBlank(result)) {
        		logger.error("阿里云发送短信失败!短信接收人={}, 传入参数={},需要生成URL的参数={}", phone, param , paras);
        		return Base.FAIL;
        	} else {
        		Map resultMap = SerializerUtil.deserializeObj(result, Map.class);
        		if (resultMap == null || resultMap.size() < 1) {
        			logger.error("阿里云发送短信失败!短信接收人={}, 传入参数={},需要生成URL的参数={}", phone, param , paras);
        			return Base.FAIL;
        		} else {
        			String code = resultMap.get("Code");
        			if (StringUtils.isNotBlank(code) && StringUtils.equals(code, "OK")){
        				return Base.SUCCESS;
        			} else {
        				logger.error("阿里云发送短信失败!短信接收人={}, 传入参数={},需要生成URL的参数={},返回错误信息={}", phone, param , paras, result);
        				return Base.FAIL;
        			}
        		}
        	}
		} catch (Exception e) {
			logger.error("阿里云发送短信程序异常!短信接收人={}, 传入参数={},需要生成URL的参数={}", phone, param , paras, e);
			return Base.FAIL;
		}
	}

	/**
	 * 根据要求替换字符
	 * @param value
	 * @return
	 * @throws Exception
	 * @author wangyijie
	 * @data 2018年1月10日
	 */
	public String specialUrlEncode(String value) throws Exception {
			return URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
	}
	
	/**
	 * 生成签名
	 * @param accessSecret
	 * @param stringToSign
	 * @return
	 * @throws Exception
	 * @author wangyijie
	 * @data 2018年1月10日
	 */
    public String sign(String accessSecret, String stringToSign) throws Exception {
        Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
        mac.init(new SecretKeySpec(accessSecret.getBytes("UTF-8"), "HmacSHA1"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        return new BASE64Encoder().encode(signData);
    }
}


你可能感兴趣的:(JAVA,阿里云,短信发送)