阿里云短信发送接口直接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<Messagequeue> {
	
	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<String, String> 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<String, String> paras = new HashMap<String, String>();
		
		//固定参数
        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<String, String> sortParas = new TreeMap<String, String>();
        sortParas.putAll(paras);
        
        try {
        	//构造待签名的字符串
        	Iterator<String> 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<String, String> 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);
    }
}


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
好的,我可以给你提供一份简单的代码示例。首先,你需要在前端页面上创建一个发送验证码的按钮,并为其绑定一个点击事件。当用户点击该按钮时,前端将通过 AJAX 请求向后端发送请求,后端会调用阿里云短信接口发送验证码。同时,我们需要记录用户发送验证码的时间,以便在5分钟内禁止用户重复发送验证码。 以下是一个简单的代码示例,你可以根据自己的需求进行调整: HTML 代码: ``` <input type="text" id="phone" placeholder="请输入手机号"> <button id="send-code">发送验证码</button> ``` JavaScript 代码: ``` const sendCodeBtn = document.getElementById('send-code'); let lastSendTime = 0; sendCodeBtn.addEventListener('click', () => { const phone = document.getElementById('phone').value; if (!phone) { alert('请输入手机号'); return; } // 判断是否在5分钟内重复发送验证码 const currentTime = Date.now(); if (currentTime - lastSendTime < 5 * 60 * 1000) { alert('请勿重复发送验证码'); return; } // 发送 AJAX 请求,请求后端发送验证码 fetch('/send-code', { method: 'POST', body: JSON.stringify({ phone }), headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.code === 0) { alert('验证码已发送,请注意查收'); lastSendTime = currentTime; } else { alert('发送验证码失败'); } }) .catch(e => { console.error(e); alert('发送验证码失败'); }); }); ``` 在上述代码中,我们首先获取用户输入的手机号,并判断是否为空。然后,我们记录了上一次发送验证码的时间,并判断是否在5分钟内重复发送验证码。如果用户满足条件,则会向服务发送一个 POST 请求,请求服务发送验证码。在服务器端,我们可以调用阿里云短信接口发送验证码。 注意:上述代码仅供参考,你需要根据实际情况进行调整。同时,你需要在后端实现一个 `/send-code` 的 API,用于接收前端发送的请求,并调用阿里云短信接口发送验证码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Run_the_ant

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值