阿里短信服务,用淘宝账号打通三大运营商通信能力,以开放API方式向开发者提供短信服务。阿里短信服务具备3秒可达、99%到达率和超低资费的优势,并完美支撑了双11“2亿用户,6亿短信,8万并发”的挑战。本文将介绍阿里短信服务API的接入方法,并提供了多种编程语言的调用示例。
进入阿里云数据市场订购[阿里短信服务](
https://market.aliyun.com/products/57002003/cmapi011900.html?spm=5176.78296.419700.1.KshhBT)。阿里短信服务是后付费商品,0元即可订购,成功发送短信后再付费。资费详见商品详情页面。
订购阿里短信服务后,就可以设置短信签名和短信模板,然后可基于API Gateway SDK实现API调用。
注意:短信签名和模板是必须的,而且要通过审核后方可使用,不允许随意发送短信。
进入云市场控制台,在短信服务中点击“设置”,进入短信签名和模板的设置页面。
短信签名为验证码短信或者通知短信内容中【】内的名称,一般为公司或者产品名,如【阿里云】。
点击“签名”标签,点击“新建短信签名”,根据下方文字说明,新建签名。
企业用户首先要确保阿里云账户通过企业实名认证,如果当前是个人账户,请先升级为企业实名认证。
新建签名时,要上传 企业营业执照,组织机构代码证、税务登记证 三个证件的图片,如果是三证合一,则上传三张同样的图片即可。如果是要使用他人公司的名称,则要上传授权委托书、授权单位的组织机构代码证的图片。
签名提交后,要等待审核,一般在1个工作日内完成。审核通过的签名方能使用。签名审核通过后,请留意“签名名称”,在API调用的时候将会使用到这个参数。
个人用户最多可以创建1个自定义短信签名;企业用户最多可以创建5个自定义短信签名。
短信模板就是短信正文的模板。
点击“模板”标签,点击“新建模板”,根据正文文字说明,新建模板。
填写内容确定之后,点击“提交审核”。审核将在1个工作日内完成。同样,只有审核通过的模板才能使用。模板审核通过后,请留意“模板CODE”,在API调用的时候将会使用到这个参数。
模板最多可添加20个。
为了保证安全,API请求需要有带上签名。API Gateway的签名机制比较复杂,一般用户可直接使用SDK和参考下一章节的调用示例直接调用短信服务API。如果你使用的编程语言,我们还没有提供SDK和示例,则需要你参考以下文档,自行实现签名。
API Gateway 请求签名机制说明
API调用的SDK及调用示例将在下一章节介绍。
1)API基础参数
API基础参数有Host、Path、请求方式、返回类型、请求参数、返回示例、错误码等等,可通过商品详情页查看。
2)AppCode或者AppKey&AppSecret
从云市场控制台 获取AppCode或者AppKey&AppSecret。
3)签名名称及模板CODE
进入云市场控制台 ,选择短信服务,点击“设置”进入短信服务控制台,可获取签名名称和模板CODE。
签名名称:
模板CODE:
4)模板变量
模板变量为新建模板的时你自定义的变量。这些变量的值可在调用API时进行设置。API参数中ParamString
即为模板变量,形式为JSON字符串。其中,数字必须转换为字符串。个人用户ParamString
变量长度必须小于15个字符。
举例:
若短信模板为:“你好,你的验证码为:${no},不要告诉别人哦”,此时你如果将参数ParamString=‘{“no”:”123456”}’
,用户收到的短信内容将为:【短信签名】你好,你的验证码为:123456,不要告诉别人哦。
提示:如果你的模板中没有变量,则ParamString={}
。
5)目标手机号
支持批量发送给多个手机号。参数名为RecNum
,多个手机号之间以英文逗号分割。
短信服务API调用成功后,则会产出账单,进入云市场控制台 ,选择短信服务,点击“账单明细”可查看账单。
账单5分钟产出一次,账单产出后,会自己从你的云账户中扣费。若扣费失败,则会中止服务,请及时充值。
数据市场现已支持两种API调用方式,一种为APPCODE简单身份认证模式,另一种为AppKey&AppSecret签名认证模式。推荐使用APPCODE模式,简单易用。如果你对安全有更高要求,则可以继续采用AppKey&AppSecret签名认证模式。
APPCODE简单身份认证模式,用起来很简单,只需要在请求的Header中加入APPCODE即可,具体可直接参考详情页中给出的调用示例代码。 需要注意的事,URL中若有中文字符,需要进行URLEncode。示例代码举例如下:
curl -i --get --include 'http://sms.market.alicloudapi.com/singleSendSms?ParamString=%7B%22no%22%3A%22123456%22%7D&RecNum=RecNum&SignName=SignName&TemplateCode=TemplateCode' -H 'Authorization:APPCODE 你自己的AppCode'
import urllib, urllib2, sys
host = 'http://sms.market.alicloudapi.com'
path = '/singleSendSms'
method = 'GET'
appcode = '你自己的AppCode'
querys = 'ParamString=%7B%22no%22%3A%22123456%22%7D&RecNum=RecNum&SignName=SignName&TemplateCode=TemplateCode'
bodys = {}
url = host + path + '?' + querys
request = urllib2.Request(url)
request.add_header('Authorization', 'APPCODE ' + appcode)
response = urllib2.urlopen(request)
content = response.read()
if (content):
print(content)
Java版本依赖于Java SDK,请注意代码其中的注释说明,下载Java SDK后使用以下代码。
public static void main(String[] args) {
String host = "http://sms.market.alicloudapi.com";
String path = "/singleSendSms";
String method = "GET";
Map headers = new HashMap();
headers.put("Authorization", "APPCODE 你自己的AppCode");
Map querys = new HashMap();
querys.put("ParamString", "%7B%22no%22%3A%22123456%22%7D");
querys.put("RecNum", "RecNum");
querys.put("SignName", "SignName");
querys.put("TemplateCode", "TemplateCode");
try {
/**
* 重要提示如下:
* HttpUtils请从
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
* 下载
*
* 相应的依赖请参照
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
*/
HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);
System.out.println(response.toString());
//获取response的body
//System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
}
其他语言,如PHP、C#、Objective-C,请直接参考商品详情页中的描述。
1)下载API Gateway Java SDK
2)在 src/main/java/com/aliyun/api/gateway/demo
目录下新建代码文件SingleSendSms.java,复制如下代码(自行修改相关参数):
package com.aliyun.api.gateway.demo;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.api.gateway.demo.constant.Constants;
import com.aliyun.api.gateway.demo.constant.HttpSchema;
import com.aliyun.api.gateway.demo.enums.Method;
public class SingleSendSms {
private final static String APP_KEY = "AppKey"; //AppKey从控制台获取
private final static String APP_SECRET = "AppSecret"; //AppSecret从控制台获取
private final static String SIGN_NAME = "签名名称"; // 签名名称从控制台获取,必须是审核通过的
private final static String TEMPLATE_CODE = "模板CODE"; //模板CODE从控制台获取,必须是审核通过的
private final static String HOST = "sms.market.alicloudapi.com"; //API域名从控制台获取
private final static String ERRORKEY = "errorMessage"; //返回错误的key
// @phoneNum: 目标手机号,多个手机号可以逗号分隔;
// @params: 短信模板中的变量,数字必须转换为字符串,如短信模板中变量为${no}",则参数params的值为{"no":"123456"}
public void sendMsg(String phoneNum, String params){
String path = "/singleSendSms";
Request request = new Request(Method.GET, HttpSchema.HTTP + HOST, path, APP_KEY, APP_SECRET, Constants.DEFAULT_TIMEOUT);
//请求的query
Map querys = new HashMap();
querys.put("SignName", SIGN_NAME);
querys.put("TemplateCode", TEMPLATE_CODE);
querys.put("RecNum", phoneNum);
querys.put("ParamString", params);
request.setQuerys(querys);
try {
Map bodymap = new HashMap();
Response response = Client.execute(request);
//根据实际业务需要,调整对response的处理
if (null == response) {
System.out.println("no response");
} else if (200 != response.getStatusCode()) {
System.out.println("StatusCode:" + response.getStatusCode());
System.out.println("ErrorMessage:"+response.getErrorMessage());
System.out.println("RequestId:"+response.getRequestId());
} else {
bodymap = ReadResponseBodyContent(response.getBody());
if (null != bodymap.get(ERRORKEY)) {
//当传入的参数不合法时,返回有错误说明
System.out.println(bodymap.get(ERRORKEY));
} else {
//成功返回map,对应的key分别为:message、success等
System.out.println(JSON.toJSONString(bodymap));
}
}
}catch (Exception e){
System.out.println(e.getMessage());
}
}
private Map ReadResponseBodyContent(String body) {
Map map = new HashMap();
try {
JSONObject jsonObject = JSON.parseObject(body);
if (null != jsonObject) {
for(Entry entry : jsonObject.entrySet()){
map.put(entry.getKey(), entry.getValue().toString());
}
}
if ("false".equals(map.get("success"))) {
map.put(ERRORKEY, map.get("message"));
}
} catch (Exception e) {
map.put(ERRORKEY, body);
}
return map;
}
public static void main(String agrs[]){
SingleSendSms app = new SingleSendSms();
app.sendMsg("18600000000,13800000000","{'name':'David'}");
}
}
3)编译后执行SingleSendSms类即可发送短信。
1) 下载API Gateway Python SDK
2) 修改 com/aliyun/api/gateway/sdk/ClientDemo.py
的代码为(自行替换相应的参数内容):
#!/usr/bin/env python
#encoding=utf-8
from com.aliyun.api.gateway.sdk import client
from com.aliyun.api.gateway.sdk.http import request
from com.aliyun.api.gateway.sdk.common import constant
host = "http://sms.market.alicloudapi.com"
url = "/singleSendSms" \
+ '?ParamString={"name":"XXXX"}' \
+ "&RecNum=18600000000,13500000000" \
+ "&SignName=签名名称" \
+ "&TemplateCode=模板CODE"
req = request.Request(host=host, url=url, method="GET", time_out=30000)
cli = client.DefaultClient(app_key="APP_KEY", app_secret="APP_SECRET")
print cli.execute(req)
3)执行 com/aliyun/api/gateway/sdk/ClientDemo.py 即可发送短信。
如果不想使用Python SDK,则可以使用如下更为精简的代码自行实现API签名和API调用:
#!/usr/bin/env python
#encoding=utf-8
import time
import base64
import hashlib
import httplib
import uuid
import hmac
class SMSClient:
def __init__(self, app_key, app_secret):
self.__app_key, self.__app_secret = app_key, app_secret
def send(self, receiver, sign, template_code, parameters=''):
print receiver, sign, template_code, parameters
self.__host = 'sms.market.alicloudapi.com'
self.__str_uri = '/singleSendSms?ParamString=%s&RecNum=%s&SignName=%s&TemplateCode=%s' % (parameters, receiver, sign, template_code)
print self.__str_uri
self.build_headers()
self.__connection = httplib.HTTPConnection(self.__host, 80)
self.__connection.connect()
self.__connection.request('GET', self.__str_uri, headers=self.__headers)
response = self.__connection.getresponse()
print response.status, response.getheaders(), response.read()
def build_headers(self):
headers = dict()
headers['X-Ca-Key'] = self.__app_key
headers['X-Ca-Nonce'] = str(uuid.uuid4())
headers['X-Ca-Timestamp'] = str(int(time.time() * 1000))
headers['X-Ca-Signature-Headers'] = 'X-Ca-Key,X-Ca-Nonce,X-Ca-Timestamp'
str_header = '\n'.join('%s:%s' % (k, headers[k]) for k in ['X-Ca-Key','X-Ca-Nonce','X-Ca-Timestamp'])
str_to_sign = '%s\n\n\n\n\n%s\n%s' % ('GET', str_header, self.__str_uri)
headers['X-Ca-Signature'] = self.__get_sign(str_to_sign, self.__app_secret)
self.__headers = headers
def __get_sign(self, source, secret):
h = hmac.new(secret, source, hashlib.sha256)
signature = base64.encodestring(h.digest()).strip()
return signature
cli = SMSClient(app_key="APP_KEY", app_secret="APP_SECRET")
cli.send('18600000000,13500000000', '签名名称', '模板CODE', '{"name":"XXXX"}')
Shell版本的代码比较精简,替换相关参数后就可以直接使用。其他语言也可以通过调用Shell脚本发送短信。
#!/usr/bin/env bash
RECEIVER=136******** #接收方手机号
SIGN="签名名称" #签名
TEMP_CODE="模板CODE" #短信模板
PARAMS="{\"time\":\"`date +%Y%m%d%H%M%S`\"}" #模板参数(json格式)
K="2******6" #AppKey,从管理控制台获取,下同
S="0******************************7" #AppSecret
NL="
"
[ "x`uname`" = "xDarwin" ] && {
NONCE="`uuidgen`"
TIMESTAMP="`date +%s`500"
} || {
NONCE="`uuid`"
TIMESTAMP="`date +%s%3N`"
}
STR_HEADER="X-Ca-Key:$K${NL}X-Ca-Nonce:$NONCE${NL}X-Ca-Timestamp:$TIMESTAMP"
STR_URI="/singleSendSms?ParamString=$PARAMS&RecNum=$RECEIVER&SignName=$SIGN&TemplateCode=$TEMP_CODE"
STR_TO_SIGN="GET${NL}${NL}${NL}${NL}${NL}$STR_HEADER${NL}$STR_URI"
SIGN="`/bin/echo -n "$STR_TO_SIGN" | openssl dgst -sha256 -hmac "$S" | sed 's/.* //g' | xxd -r -p | base64`"
STR_URI="`echo "$STR_URI" | sed 's#{#\\\\{#g;s#}#\\\\}#g'`"
curl -v -H 'Accept:' \
-H "X-Ca-Key: $K" \
-H "X-Ca-Nonce: $NONCE" \
-H "X-Ca-Timestamp: $TIMESTAMP" \
-H "X-Ca-Signature-Headers: X-Ca-Key,X-Ca-Nonce,X-Ca-Timestamp" \
-H "X-Ca-Signature: $SIGN" \
"http://sms.market.alicloudapi.com$STR_URI"
1)下载API Gateway .NET SDK
2)新建一个工程,并依赖于.NET SDK,再新建一个SingleSendSms类,代码如下(自行修改相关参数):
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Net;
namespace example
{
using aliyun_api_gateway_sdk.Constant;
using aliyun_api_gateway_sdk.Util;
class SingleSendSms
{
private const String appKey = "************";
private const String appSecret = "****************************";
private const String host = "http://sms.market.alicloudapi.com";
private const String path = "/singleSendSms";
public static void Main(string[] args)
{
var headers = new Dictionary();
var querys = new Dictionary();
var signHeader = new List();
//设定Content-Type,根据服务器端接受的值来设置
headers.Add(HttpHeader.HTTP_HEADER_CONTENT_TYPE, ContentType.CONTENT_TYPE_TEXT);
//设定Accept,根据服务器端接受的值来设置
headers.Add(HttpHeader.HTTP_HEADER_ACCEPT, ContentType.CONTENT_TYPE_TEXT);
//注意:业务query部分,如果没有则无此行;请不要、不要、不要做UrlEncode处理
querys.Add("ParamString", "{'name':'XXXX'}"); // 模板变量
querys.Add("RecNum", "18600000000,18600000000"); // 接收手机号,多个手机号以英文逗号分隔
querys.Add("SignName", "签名名称"); // 短信签名名称
querys.Add("TemplateCode", "模板CODE"); // 短信模板CODE
using (HttpWebResponse response = HttpUtil.HttpGet(host, path, appKey, appSecret, 30000, headers, querys, signHeader)) {
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.Method);
Console.WriteLine(response.Headers);
Stream st = response.GetResponseStream();
var reader = new StreamReader(st, Encoding.GetEncoding("utf-8"));
Console.WriteLine(reader.ReadToEnd());
Console.WriteLine(Constants.LF);
}
Console.Read();
}
}
}
3)执行SingleSendSms中的Main函数就可以发送短信。
PHP版本的代码比较精简,替换相关参数后就可以直接使用。
'{"name":"XXXX"}',
'RecNum' => '18600000000,13500000000',
'SignName' =>'签名名称',
'TemplateCode' => '模板CODE'
);
$request_host = "http://sms.market.alicloudapi.com";
$request_uri = "/singleSendSms";
$request_method = "GET";
$info = "";
$content = do_get($app_key, $app_secret, $request_host, $request_uri, $request_method, $request_paras, $info);
print_r($content); // API返回值
# print_r($info); // 系统请求返回信息
function do_get($app_key, $app_secret, $request_host, $request_uri, $request_method, $request_paras, &$info) {
ksort($request_paras);
$request_header_accept = "application/json;charset=utf-8";
$content_type = "";
$headers = array(
'X-Ca-Key' => $app_key,
'Accept' => $request_header_accept
);
ksort($headers);
$header_str = "";
$header_ignore_list = array('X-CA-SIGNATURE', 'X-CA-SIGNATURE-HEADERS', 'ACCEPT', 'CONTENT-MD5', 'CONTENT-TYPE', 'DATE');
$sig_header = array();
foreach($headers as $k => $v) {
if(in_array(strtoupper($k), $header_ignore_list)) {
continue;
}
$header_str .= $k . ':' . $v . "\n";
array_push($sig_header, $k);
}
$url_str = $request_uri;
$para_array = array();
foreach($request_paras as $k => $v) {
array_push($para_array, $k .'='. $v);
}
if(!empty($para_array)) {
$url_str .= '?' . join('&', $para_array);
}
$content_md5 = "";
$date = "";
$sign_str = "";
$sign_str .= $request_method ."\n";
$sign_str .= $request_header_accept."\n";
$sign_str .= $content_md5."\n";
$sign_str .= "\n";
$sign_str .= $date."\n";
$sign_str .= $header_str;
$sign_str .= $url_str;
$sign = base64_encode(hash_hmac('sha256', $sign_str, $app_secret, true));
$headers['X-Ca-Signature'] = $sign;
$headers['X-Ca-Signature-Headers'] = join(',', $sig_header);
$request_header = array();
foreach($headers as $k => $v) {
array_push($request_header, $k .': ' . $v);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request_host . $url_str);
//curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_header);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ret = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return $ret;
}
1)新建一个Signature类,类的实现代码如下(请自行修改appKey和appSecret):
#import "Signature.h"
#import
static NSString * const host = @"http://sms.market.alicloudapi.com"; // HOST
static NSString * const appKey = @"********"; // app key,从云市场控制台获取,下同
static NSString * const appSecret = @"***************************************"; // app secret
@implementation Signature
/*! @brief send message
@param receiver 接收者的手机号, 多个手机号码以英文逗号分隔 e.g: 13000000000
@param sign 签名名称, e.g: 数据市场
@param templateCode 模板CODE e.g: MSG_0000000
@param paramters 模板参数,JSON字符串格式 e.g: {"name" : "Hello"}
*/
- (void)sendMsg:(NSString *)receiver :(NSString *)sign :(NSString *)templateCode :(NSString *)paramters {
NSString *pathPamras = [NSString stringWithFormat:@"/singleSendSms?ParamString=%@&RecNum=%@&SignName=%@&TemplateCode=%@", paramters, receiver, sign, templateCode];
/*! @warn 因为请求中带有json数据,花括号不被nsurl接受,所以这里计算签名的时候不能转义,拼接到URL上之后才转义 */
NSString *urlString = [host stringByAppendingString:[pathPamras stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
NSLog(@"url: %@", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSString *contentType = @"";
NSString *accept = @"";
NSString *contentMd5 = @"";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSDictionary *header = [self getHeader:pathPamras:@"GET":contentType:accept:contentMd5];
[request setAllHTTPHeaderFields:header];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *sessionTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"result: %@", body);
}];
[sessionTask resume];
}
/*! @brief 获取header
@param pathParams 请求url路径及其query参数
@param method 请求方法名
@param contentType header中的ContentType, 服务端会将此字段加入签名,某些http客户端会自动加上默认值,这里强制指定,防止签名报错。
@param accept 同contentType
@param contentMd5 当body为非Form表单形式的时候需要计算MD5值
*/
- (NSDictionary *)getHeader :(NSString *)pathParams :(NSString *)method :(NSString *)contentType :(NSString *)accept :(NSString *)contentMd5 {
NSString *uuid = [[NSUUID UUID] UUIDString];
UInt64 recordTime = [[NSDate date] timeIntervalSince1970] * 1000;
NSNumber *currentTime = [NSNumber numberWithInteger:recordTime];
NSString *timestamp = [NSString stringWithFormat:@"%@", currentTime];
method = [method uppercaseString];
NSString *headerString = [NSString stringWithFormat:@"X-Ca-Key:%@\nX-Ca-Nonce:%@\nX-Ca-Timestamp:%@", appKey, uuid, timestamp];
NSString *sinatureString = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@\n%@\n%@", method, accept, contentMd5, contentType, @"", headerString, pathParams];
NSDictionary *header = @{
@"X-Ca-Key" : appKey,
@"X-Ca-Nonce" : uuid,
@"X-Ca-Timestamp" : timestamp,
@"X-Ca-Signature-Headers" : @"X-Ca-Key,X-Ca-Nonce,X-Ca-Timestamp",
@"X-Ca-Signature" : [self encrypto:sinatureString],
/*! @warn IOS默认会把Accept带上"*斜杠* 会导致签名失败,这里需要强制指定accept" */
@"Accept" : accept,
@"Content-type" : contentType
};
return header;
}
- (NSString *)encrypto:(NSString *)sourceString {
NSData *saltData = [appSecret dataUsingEncoding:NSUTF8StringEncoding];
NSData *paramData = [sourceString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH ];
CCHmac(kCCHmacAlgSHA256, saltData.bytes, saltData.length, paramData.bytes, paramData.length, hash.mutableBytes);
NSData *base64hash = [hash base64EncodedDataWithOptions:0];
NSString *base64String = [[NSString alloc] initWithData:base64hash encoding:NSUTF8StringEncoding];
return base64String;
}
@end
调用方法:
#import "Sinagure.h"
Sinagure *sinagure = [[Sinagure alloc] init];
[sinagure sendMsg:@"13000000000":@"签名名称":@"模板code":@"{\"name\":\"xxx\"}"];
Swift脚本可以通过调用Object-C代码实现短信发送。
如果你使用的编程语言未在上述调用示例之内,你可以根据签名说明文档自行实现。也可以通过调用Shell脚本发送短信。
如有其他问题,可以加入我们的客户支持群(群号:1639868142),有专门的小二会提供咨询。
Enjoy it~