阿里云短信服务调用详解
最近项目需求使用了阿里云的短信服务给用户发送验证,写此文章记录学习。
接口申请:
步骤 1 创建阿里云账号
为了访问短信服务,您需要有一个阿里云账号。如果没有,可首先按照如下步骤创建阿里云账号:
- 访问阿里云 官方网站,单击页面上的 免费注册 按钮。
- 按照屏幕提示完成注册流程并进行实名认证,短信服务只支持实名认证用户使用。为了更好地使用阿里云服务,建议尽快完成实名认证,否则部分阿里云服务将无法使用。具体实名认证流程,请参考 这里。
步骤 2 获取阿里云访问密钥
为了使用短信发送API-JAVA SDK,您必须申请阿里云的访问密钥。
阿里云访问秘钥是阿里云为用户使用 API(非控制台)来访问其云资源设计的“安全口令”。您可以用它来签名 API 请求内容以通过服务端的安全验证。
该访问秘钥成对(AccessKeyId 与 AccessKeySecret)生成和使用。每个阿里云用户可以创建多对访问秘钥,且可随时启用(Active)、禁用(Inactive)或者删除已经生成的访问秘钥对。
您可以通过阿里云控制台的 秘钥管理页面 创建、管理所有的访问秘钥对,且保证它处于“启用”状态。由于访问秘钥是阿里云对 API 请求进行安全验证的关键因子,请妥善保管你的访问秘钥。如果某些秘钥对出现泄漏风险,建议及时删除该秘钥对并生成新的替代秘钥对。
步骤 3 在控制台完成模板与签名的申请,获得调用接口必备的参数
短信签名
根据用户属性来创建符合自身属性的签名信息。企业用户需要上传相关企业资质证明,个人用户需要上传证明个人身份的证明。
注意:短信签名需要审核通过后才可以使用。
短信模板
短信模板,即具体发送的短信内容。
短信模板可以支持验证码、短信通知、推广短信三种模式。验证码和短信通知,通过变量替换实现个性短信定制。推广短信不支持在模板中添加变量。
短信模板需要审核通过后才可以使用。
为了成功发送短信通知,您至少需要完成以下步骤
一、在控制台完成短信签名与短信模板的申请,获得调用接口必备的参数
在“短信签名”页面完成签名的申请,获得短信签名的字符串 签名申请手册
在“短信模板”页面完成模板的申请,获得模板ID。模板申请手册
开通阿里云短信服务链接:https://www.aliyun.com/product/sms?spm=5176.doc55324.765261.224.SSoWpM
使用阿里云的短信服务,需要注册登录自己的阿里云控制台,然后进入AccessKeys的处理界面
短信验证获取 AccessKey ID和AccessKey
短信服务文档
https://help.aliyun.com/product/44282.html?spm=a2c4g.11186623.3.1.60861a81MfZAoZ
技术对接步骤
1:下载SDK工具包:SDK工具包中一共包含了2个类库,一个aliyun-java-sdk-core包,另外一个是alicom-dysms-api包
<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.0.6</version> <!-- 注:如提示报错,先升级基础包版,无法解决可联系技术支持 --> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <version>1.1.0</version> </dependency>
2: 编写样例程序
发送短信接口(SendSms)
//设置超时时间-可自行调整 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化ascClient需要的几个参数 final String product = "Dysmsapi";//短信API产品名称(短信产品名固定,无需修改) final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名(接口地址固定,无需修改) //替换成你的AK final String accessKeyId = "yourAccessKeyId";//你的accessKeyId,参考本文档步骤2 final String accessKeySecret = "yourAccessKeySecret";//你的accessKeySecret,参考本文档步骤2 //初始化ascClient,暂时不支持多region(请勿修改) IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,accessKeySecret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //组装请求对象 SendSmsRequest request = new SendSmsRequest(); //使用post提交 request.setMethod(MethodType.POST); //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000” request.setPhoneNumbers("1500000000"); //必填:短信签名-可在短信控制台中找到 request.setSignName("云通信"); //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版 request.setTemplateCode("SMS_1000000"); //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败 request.setTemplateParam("{\"name\":\"Tom\", \"code\":\"123\"}"); //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段) //request.setSmsUpExtendCode("90997"); //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者 request.setOutId("yourOutId"); //请求失败这里会抛ClientException异常 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) { //请求成功 }
参数
入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
PhoneNumbers | String | 必须 | 15000000000 | 短信接收号码,支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为:国际区号+号码,如“85200000000” |
SignName | String | 必须 | 云通信 | 短信签名 |
TemplateCode | String | 必须 | SMS_0000 | 短信模板ID,发送国际/港澳台消息时,请使用国际/港澳台短信模版 |
TemplateParam | String | 可选 | {“code”:”1234”,”product”:”ytx”} | 短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议。 |
SmsUpExtendCode | String | 可选 | 90999 | 上行短信扩展码,无特殊需要此字段的用户请忽略此字段 |
OutId | String | 可选 | abcdefgh | 外部流水扩展字段 |
出参列表
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
RequestId | String | 8906582E-6722 | 请求ID |
Code | String | OK | 状态码-返回OK代表请求成功,其他错误码详见错误码列表 |
Message | String | 请求成功 | 状态码的描述 |
BizId | String | 134523^4351232 | 发送回执ID,可根据该ID查询具体的发送状态 |
短信接口调用错误码:https://help.aliyun.com/knowledge_detail/57717.html?spm=a2c4g.11186623.2.20.37103144DVn56z
短信发送记录查询接口(QuerySendDetails)
用于查询短信发送的状态,是否成功到达终端用户手机
//设置超时时间 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //云通信产品-短信API服务产品名称(短信产品名固定,无需修改) final String product = "Dysmsapi"; //云通信产品-短信API服务产品域名(接口地址固定,无需修改) final String domain = "dysmsapi.aliyuncs.com"; //此处需要替换成开发者自己的AK信息 final String accessKeyId = "yourAccessKeyId"; final String accessKeySecret = "yourAccessKeySecret"; //初始化ascClient 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("150000000"); //可选-调用发送短信接口时返回的BizId request.setBizId("1234567^8901234"); //必填-短信发送的日期 支持30天内记录查询(可查其中一天的发送数据),格式yyyyMMdd request.setSendDate("20170513"); //必填-页大小 request.setPageSize(10L); //必填-当前页码从1开始计数 request.setCurrentPage(1L); //hint 此处可能会抛出异常,注意catch QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request); //获取返回结果 if(querySendDetailsResponse.getCode() != null && querySendDetailsResponse.getCode().equals("OK")){ //代表请求成功 }
入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
PhoneNumber | String | 必须 | 15000000000 | 短信接收号码,如果需要查询国际短信,号码前需要带上对应国家的区号,区号的获取详见国际短信支持国家信息查询API接口 |
BizId | String | 可选 | 1234^1234 | 发送流水号,从调用发送接口返回值中获取 |
SendDate | String | 必须 | 20170525 | 短信发送日期格式yyyyMMdd,支持最近30天记录查询 |
PageSize | Number | 必须 | 10 | 页大小Max=50 |
CurrentPage | Number | 必须 | 1 | 当前页码 |
出参列表
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
RequestId | String | 8906582E-6722 | 请求ID |
Code | String | OK | 状态码-返回OK代表请求成功,其他错误码详见错误码列表 |
Message | String | 请求成功 | 状态码的描述 |
TotalCount | Number | 100 | 发送总条数 |
smsSendDetailDTOs | Object | - | 发送明细结构体,详见Demo样例 |
Demo样例
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
phoneNum | String | 13000000000 | 手机号码 |
sendStatus | Number | 1 | 发送状态 1:等待回执,2:发送失败,3:发送成功 |
errCode | String | DELIVERED | 运营商短信错误码 |
templateCode | String | SMS_000 | 模板ID |
content | String | 【阿里云】你好! | 短信内容 |
sendDate | Object | 2017-05-25 00:00:00 | 发送时间 |
receiveDate | String | 2017-05-25 00:00:00 | 接收时间 |
outId | String | 123 | 外部流水扩展字段 |
发送批量短信接口(SendBatchSms)
//设置超时时间-可自行调整 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化ascClient需要的几个参数 final String product = "Dysmsapi";//短信API产品名称(短信产品名固定,无需修改) final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名(接口地址固定,无需修改) //替换成你的AK final String accessKeyId = "yourAccessKeyId";//你的accessKeyId,参考本文档步骤2 final String accessKeySecret = "yourAccessKeySecret";//你的accessKeySecret,参考本文档步骤2 //初始化ascClient,暂时不支持多region(请勿修改) IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //组装请求对象 SendBatchSmsRequest request = new SendBatchSmsRequest(); //使用post提交 request.setMethod(MethodType.POST); //必填:待发送手机号。支持JSON格式的批量调用,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 request.setPhoneNumberJson("[\"1500000000\",\"1500000001\"]"); //必填:短信签名-支持不同的号码发送不同的短信签名 request.setSignNameJson("[\"云通信\",\"云通信\"]"); //必填:短信模板-可在短信控制台中找到 request.setTemplateCode("SMS_1000000"); //必填:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败 request.setTemplateParamJson("[{\"name\":\"Tom\", \"code\":\"123\"},{\"name\":\"Jack\", \"code\":\"456\"}]"); //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段) //request.setSmsUpExtendCodeJson("[\"90997\",\"90998\"]"); //请求失败这里会抛ClientException异常 SendBatchSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) { //请求成功 }
参数
入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
PhoneNumberJson | String | 必须 | [“15000000000”,”15000000001”] | 短信接收号码,JSON格式,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 |
SignNameJson | String | 必须 | [“云通信”,”云通信”] | 短信签名,JSON格式 |
TemplateCode | String | 必须 | SMS_0000 | 短信模板ID |
TemplateParamJson | String | 必须 | [{“code”:”1234”,”product”:”ytx1”},{“code”:”5678”,”product”:”ytx2”}] | 短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议。 |
SmsUpExtendCodeJson | String | 可选 | [“90999”,”90998”] | 上行短信扩展码,JSON格式,无特殊需要此字段的用户请忽略此字段 |
出参列表
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
RequestId | String | 8906582E-6722 | 请求ID |
Code | String | OK | 状态码-返回OK代表请求成功,其他错误码详见错误码列表 |
Message | String | 请求成功 | 状态码的描述 |
BizId | String | 134523^4351232 | 发送回执ID,可根据该ID查询具体的发送状态 |
短信消息API
当您使用短信的API接口发送短信后,可以通过使用MNS的Queue模型来接收短信的回执消息,假如服务出现异常情况时(如网络问题),导致消息回执未成功获取,还可以通过短信发送记录查询API接口进行一定的补偿(目前支持30天内发送记录的查询,可查询一天的发送数据)。
消息的订阅
- 云通信的所有业务消息都通过MNS消息服务向外发送。用户每订阅一个类别的消息(比如上行短信消息SmsUp),系统都会为用户分配一个独立的消息队列。
- 用户可以通过阿里云账号拿到一个临时的token用于获取队列中的消息。用户可以下载demo,编写简单的消息处理类即可完成消息处理的任务。
- 在页面上订阅消息,订阅完消息后,能拿到消息队列名称(queueName)。比如:Alicom-Queue-xxxxxx-SmsReport 。队列名字每个用户都不同。
路径:短信产品进入控制台→应用开发→接口调用→云通信消息接收→短信状态报告接收→向右滑动(MNS消息队列消费模式。接收消息队列信息,请下载消息服务SDK进行程序编写,详见API文档)
消息类型
短信提供2种消息类型SmsReport(短信回执报告消息) 和 SmsUp(上行短信消息)
- 通过订阅SmsReport短信状态报告,可以获知每条短信的发送情况,了解短信是否达到终端用户的状态与相关信息
- 通过订阅SmsUp上行短信消息,可以获知终端用户回复短信的内容
-
短信回执消息SmsReport消息体格式
名称 类型 描述 示例 phone_number String 短信接收号码 13000000000 success Boolean 发送是否成功 true biz_id String 发送回执ID 1234^345 out_id String 调用发送短信接口时传的outId 123456 send_time String 转发给运营商的时间 2017-06-01 10:00:00 report_time String 收到运营商回执的时间 2017-06-01 10:00:05 err_code String 错误码 UNKNOW err_msg String 错误信息 未知异常 sms_size String 140字节算一条短信,短信长度超过140字节时会拆分成多条短信发送 1,2,3 上行短信消息SmsUp
名称 类型 描述 示例 phone_number String 短信接收号码 13000000000 content String 短信内容 true sign_name String 短信签名 【阿里云】 send_time String 时间 2017-06-01 10:00:00 dest_code String 扩展码 123456 sequence_id Double 消息序列ID 123456
/** * 只能用于接收云通信的消息,不能用于接收其他业务的消息 */ public class ReceiveAlicomMsgDemo { private static Log logger=LogFactory.getLog(ReceiveAlicomMsgDemo.class); //以短信回执消息处理为例 static class MyMessageListener implements MessageListener{ private Gson gson=new Gson(); @Override public boolean dealMessage(Message message) { System.out.println("message handle: " + message.getReceiptHandle()); System.out.println("message body: " + message.getMessageBodyAsString()); System.out.println("message id: " + message.getMessageId()); System.out.println("message dequeue count:" + message.getDequeueCount()); //以短信回执消息处理为例 try{ Map<String,Object> contentMap=gson.fromJson(message.getMessageBodyAsString(), HashMap.class); String phoneNumber=(String)contentMap.get("phone_number"); Boolean success=(Boolean)contentMap.get("success"); String bizId=(String)contentMap.get("biz_id"); String outId=(String)contentMap.get("out_id"); String sendTime=(String)contentMap.get("send_time"); String reportTime=(String)contentMap.get("report_time"); String errCode=(String)contentMap.get("err_code"); String errMsg=(String)contentMap.get("err_msg"); //TODO 这里开始写业务代码 }catch(com.google.gson.JsonSyntaxException e){ logger.error("error_json_format:"+message.getMessageBodyAsString(),e); } Boolean dealResult=true; return dealResult;//返回true,则工具类自动删除已拉取的消息。 } } public static void main(String[] args) throws com.aliyuncs.exceptions.ClientException, ParseException { DefaultAlicomMessagePuller puller=new DefaultAlicomMessagePuller(); String accessKeyId="yourAccessKeyId"; String accessKeySecret="yourAccessKeySecret"; String messageType="SmsReport";//短信回执:SmsReport,短信上行:SmsUp String queueName="yourQueueName";//在云通信页面开通相应业务消息后,就能在页面上获得对应的queueName puller.startReceiveMsg(accessKeyId,accessKeySecret ,messageType,queueName , new MyMessageListener()); } }