简介:面向需要快速接入阿里云短信验证码、通知推送等场景的后端开发者,这份示例工程提供了从账号准备到代码调用的完整参考。压缩包仅485KB,包含92个文件,以C#源码(68个cs)和依赖程序集(18个dll)为主,另有工程配置文件、XML说明和文本指南,可直接在Visual Studio中打开运行,省去自行搭建环境的麻烦。内容覆盖阿里云短信服务的注册与实例创建、AccessKey密钥管理、SendSms接口参数构造、模板与签名配置等关键环节,并给出基于.NET的发送示例,以及针对网络异常、超时和错误码的重试处理策略,帮助规避接口对接中的常见坑点。工程内的demo目录和sdk库结构清晰,方便对照官方API文档理解调用细节,也适合用作后续项目的基础框架。目前已有579人学习下载,适合具备基础C#或.NET知识、希望快速落地短信验证/通知功能的开发者。 短信接口这东西,做后端开发的迟早会遇到。登录要验证码,支付要通知,服务器出问题要告警,业务关键节点要提醒……短信这个能力,看起来不起眼,但没它还真的不行。国内主流的短信服务商里,阿里云短信接口是大家用得最多的之一,原因倒不复杂:文档清晰、SDK覆盖主流语言、接入门槛低,而且按量付费,几百条验证码的成本可以忽略不计。这篇文章我把整个接入过程从头讲一遍,从控制台配置到Java代码实现,再到排错心得,争取让你照着走一遍就能跑通。
这篇内容适合谁看?如果你正准备在项目里集成短信功能,之前没接触过阿里云短信,那完全可以按我的步骤走。如果你已经接过了,但总觉得里面有些概念没弄清楚,比如签名为啥总审核不过、模板变量怎么传才对、返回的错误码到底什么意思,那你可以直接跳到后面的章节,对照排查。这里没有花里胡哨的东西,全是实际开发中的经验。
1. 接入短信服务之前,先搞清这3个基础概念
1.1 为什么选择阿里云短信
市面上的短信服务商不少,我为什么一直推荐阿里云?第一,SDK和文档全。Java、Python、Go、Node.js、PHP、C++都覆盖,对于团队来说无论技术栈是什么,接起来都很快。第二,接口设计稳定。短信服务本身非常依赖稳定性,阿里云短信的API设计这么多年没有大的破坏性变更,升级成本低。第三,链路完善。除了发送短信,还有回执查询、消息订阅、退订管理等一系列能力,你一个服务商就能把整个短信生命周期管理起来。
另外,阿里云短信对个人开发者也很友好。不需要你有公司主体,个人实名认证之后就能开通服务,甚至有免费的测试签名和模板可申请,这对刚开始练手或者做个人项目的人来说非常实用。我的建议是,除非你有非常特殊的通道需求,否则短信服务这种基础设施,优先选大厂,省心。
1.2 三个关键词:AccessKey、签名、模板
在动手写代码之前,必须把三个概念搞清楚,否则你会被后续各种报错绕晕。
第一个是AccessKey,你可以把它理解成你的API钥匙,由AccessKey ID和AccessKey Secret两部分组成。调用阿里云任何OpenAPI,都要用钥匙做身份认证。这个钥匙非常重要,泄露了别人就能用你的账号资源,所以不要放在前端代码里,也不要用git提交到仓库,建议在服务端通过环境变量或者配置中心管理。
第二个是签名。就是你短信开头中括号里的那个名字,比如【阿里云】。签名是给用户看的发送方名称,在阿里云控制台申请,需要提交资料审核。审核通过之后,调用接口时需要传入。
第三个是模板。短信正文内容的格式定义,比如“您的验证码为${code},${minutes}分钟内有效”。模板里允许放变量,用${}占位。模板也需要审核,审核的主要目的是防止垃圾短信。
很多新手会混淆签名和模板,其实记住一句话就行:签名告诉用户你是谁,模板告诉用户你要说什么。这俩都过审核,而且审核状态不对时,发送也会报错。
1.3 整体接入流程和费用参考
整个接入流程,从零开始梳理一遍:
- 注册阿里云账号,完成实名认证
- 在控制台开通短信服务
- 创建RAM子用户,只授予短信服务的权限,获取AccessKey
- 申请短信签名,等待审核
- 申请短信模板,等待审核
- 代码集成官方SDK,调用发送接口
- 上线前用测试手机号验证,查看发送记录
整个链路里,真正花时间的不是写代码,而是签名和模板的审核。所以我的习惯是先把控制台这一步做好,再动代码,不然代码写完了审核还没过,只能干等。
费用方面,短信是按条计费的,不同场景价格不太一样,一般国内短信通知和验证码大约在0.04元/条左右,具体以官网价格为准。新用户开通后有一定免费额度,拿来调试绰绰有余。发送失败通常不收费,但审核通过不等于发送成功,具体以发送后返回的状态报告为准。
2. 控制台准备工作里,最容易被卡住的几个环节
2.1 开通短信服务并配置RAM子用户AccessKey
先登录阿里云控制台,在搜索栏输入“短信服务”,进入产品页开通。这里有一点我要单独强调:不要直接用主账号的AccessKey去做开发。主账号钥匙权限太大,万一泄露,整个账号都可能被操作。正确做法是去RAM访问控制里创建一个子用户,给它短信服务的权限就够了,最省事的是直接给子用户授予AliyunDysmsFullAccess这个系统权限。
创建完子用户后,会生成AccessKey ID和AccessKey Secret。Secret只显示一次,务必保存到安全的地方。实际操作中,很多人因为没保存,后面又重建钥匙,其实重建很简单,但在生产环境中重建钥匙意味着所有依赖旧钥匙的服务都要更新,提前处理好能省很多事。
2.2 申请签名:资料、名称、审核的一次性到位
签名的申请页面需要填几个关键项:签名内容、签名类型、应用说明。
签名内容就是短信开头【】里的几个字。个人用户一般用网站名称、App名称或者小程序名称作为签名。签名类型选择要和你的认证主体匹配,个人实名认证通常选“App”或“网站”会容易过。但要注意,如果你是个人开发,申请签名时提交的网站或应用信息,最好能对应到你真实拥有的资源,比如已经备案的域名或者已经上线的应用。
应用说明这里是很多人不重视的地方。我建议写清楚“这个签名用于什么业务场景,预计发送哪些短信”,能写具体就写具体。我见过太多人只写“用于发送短信”,审核被打回后还不知道哪里出了问题。记住,审核方最在意的是你是否有权使用这个签名,以及你的业务是否真实存在。
审核一般需要2小时到1个工作日。如果被驳回,后台会给出驳回原因,按原因修改后重新提交即可。
2.3 申请模板:变量的设计直接影响代码复杂度
模板申请的页面,核心就是模板内容。这里要特别注意变量的设计。变量用${xxx}表示,一个模板里可以有多个变量,但变量名尽量使用英文,并且含义清晰,比如${code}表示验证码、${product}表示产品名。
模板的文案同样需要符合规范,不能包含营销、违法违规的词汇。验证码类模板一般格式是“您的验证码为${code},${minutes}分钟内有效,请勿泄露。”文案越简洁越容易通过。
有一个细节是设计模板时就把变量格式定好,比如验证码位数、内容长度限制。因为模板里的变量会被替换成真实值,如果真实值和预留规则不一致,发送时会报错。举个例子,如果模板写的是“您的验证码为${code},有效期为${minutes}分钟”,那么代码里传JSON参数时,key必须叫code和minutes,严格对应,一个字母都不能错。这块是新手最容易踩的坑,因为模板审核通过后,你再去改模板又要等审核,所以在申请模板前就想好参数名。
3. 手把手跑通第一条短信:Java + 官方SDK
3.1 Maven引入依赖,顺便优化一下仓库
我平时后端用的是Java生态,这里以Java为例。阿里云短信服务官方提供的SDK叫dysmsapi,Maven坐标如下:
<dependency> <groupId>com.aliyun</groupId> <artifactId>dysmsapi20170525</artifactId> <version>2.0.24</version> </dependency>版本号建议以阿里云官方文档发布的最新稳定版为准,不要机械复制我这里的版本。如果你公司内部使用Maven中央仓库比较慢,可以配一下阿里云的Maven镜像,在settings.xml里加一个mirror:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>这个配置对于经常拉不到依赖的同学来说很实用,全国范围内访问阿里云的公共仓库速度都比较快,减少等依赖下载的时间。如果你用的是Gradle,也可以用maven { url 'https://maven.aliyun.com/repository/public' },效果同样。
3.2 初始化客户端:把配置从代码里拎出来
SDK用法比较统一,先构建一个Client对象。在2.x版本的SDK中,客户端实例用Config来配置。这里我不建议直接把AccessKey硬编码在代码里,至少要用环境变量去读取。
import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import com.aliyun.teaopenapi.models.Config; public class SmsSender { private final com.aliyun.dysmsapi20170525.Client client; public SmsSender(String accessKeyId, String accessKeySecret) { Config config = new Config() .setAccessKeyId(accessKeyId) .setAccessKeySecret(accessKeySecret); config.endpoint = "dysmsapi.aliyuncs.com"; try { client = new com.aliyun.dysmsapi20170525.Client(config); } catch (Exception e) { throw new RuntimeException("初始化短信客户端失败", e); } } }这里的endpoint是固定的,阿里云短信服务的接入地址就是dysmsapi.aliyuncs.com,不需要自己拼URL。这一点对新手很重要,因为很多旧文章还在教你用HTTP工具手动拼参数,SDK其实已经把签名算法、请求构造、响应解析都封装好了,直接用就行。
如果你用Spring Boot,可以结合@ConfigurationProperties把accessKeyId和accessKeySecret配置到配置文件里,这样换环境只需要改配置,不用重新编译代码。
3.3 发送短信:理解返回结果比会调接口更重要
初始化好client之后,发送短信就是构造一个SendSmsRequest并调用sendSms方法:
public void sendSms(String phone, String signName, String templateCode, String templateParam) { SendSmsRequest request = new SendSmsRequest() .setPhoneNumbers(phone) .setSignName(signName) .setTemplateCode(templateCode) .setTemplateParam(templateParam); SendSmsResponse response = client.sendSms(request); String code = response.getBody().getCode(); if ("OK".equals(code)) { System.out.println("短信发送成功,MessageId:" + response.getBody().getMessageId()); } else { System.out.println("发送失败:" + response.getBody().getMessage()); } }这里几个参数挨个讲一下。
phoneNumbers就是目标手机号,注意一次调用可以传多个手机号,用英文逗号分隔,但一次传太多可能触发限流,建议单次不超过100个。signName是你在控制台申请通过的签名,templateCode是模板CODE,长得像SMS_123456789这样。templateParam是模板里变量对应的实际值,格式是JSON字符串,比如模板是“您的验证码为${code}”,参数就是{"code":"123456"}。
调用结果里最重要的是code字段,返回OK就表示接口调用成功,但注意“调用成功”不等于“短信一定送达”,最终以短信回执为准。这里也是很多人容易误解的地方。关于回执的获取,既可以通过控制台查询发送详情,也可以通过接口查询,或者在业务侧关注短信回执报告。如果你就想在代码里拿到最终的发送状态,可以单独写一个轮询查询的模块,或者接入短信服务提供的消息回执功能,网上搜“接收阿里云 smsreport”能找到不少现成方案,简单说就是用一个队列接收状态报告。这个功能在验证码场景不是必须的,但对通知类、告警类业务很有价值。
我个人的做法是:验证码类短信,发送后直接返回给前端“验证码已发送”,不阻塞等待回执;重要通知,比如服务器异常告警,会用回执确保送达。两种场景对回执的依赖是不同的,你想清楚自己要哪种,再决定要不要接回执。
4. 常见问题与排查技巧实录
4.1 高频报错速查表
下面这份表格里的错误码,我基本都踩过。建议收藏,遇到问题直接对号入座。
| 错误码 | 含义 | 常见原因和解决思路 |
|---|---|---|
| isv.SMS_SIGNATURE_ILLEGAL | 签名不合法 | 签名未审核通过,或传入的签名与控制台不一致;检查签名名称是否完全一致 |
| isv.MOBILE_NUMBER_ILLEGAL | 手机号不合法 | 手机号格式问题,确认是11位国内手机号,不要带+86 |
| isv.SMS_TEMPLATE_ILLEGAL | 模板不合法 | 模板CODE不存在或未审核通过,检查模板ID是否写错 |
| isv.TEMPLATE_MISSING_PARAMETERS | 模板缺少变量 | 模板里定义了变量,但templateParam里没传,或key对不上 |
| isv.BUSINESS_LIMIT_CONTROL | 触发业务流控 | 发送频率过高,详见下面的限流说明 |
| isp.RAM_PERMISSION_DENY | 权限不足 | 子用户没有被授权发送短信,去RAM里加AliyunDysmsFullAccess权限 |
| SignatureDoesNotMatch | 签名不匹配 | 常见原因是AccessKey配错,或者服务器时间不准确,检查钥匙并同步系统时间 |
特别是SignatureDoesNotMatch,当时排查了很久,最后发现是服务器时区没设对,时间偏了几分钟导致签名串校验失败。所以遇到签名不匹配,先看时间,再看Key。
4.2 限流频率控制:业务侧防刷比后端限制更关键
阿里云短信服务对发送频率有默认限制,主要是防骚扰。以验证码短信为例,常见限制包括:同一手机号一分钟内最多1条、一小时内最多5条、一天内最多10条,具体以官方控制台配额说明为准。这个限制不是说绝对不能超,而是超过会被拒绝,返回BUSINESS_LIMIT_CONTROL。
所以业务侧必须做防刷:发送前校验手机号格式,同一个手机号设置发送间隔至少60秒,验证码有有效期,一般5到10分钟。另外验证码的校验逻辑里,我见过很多错误做法是把验证码直接存在内存里,重启就丢,也不设有效期。我的建议是至少存到Redis,设置过期时间,校验成功后立即删除,防止暴力破解。这里多提醒一句,短信验证码是黑产的重点关注对象,接口的熔断、限流和非法请求隔离一定要做。
4.3 一些提高开发效率的小细节
最后分享几个我在项目里积累的经验。
第一,把短信发送单独封装成一个组件或者Service,上层业务只管传手机号和业务参数。这样后期如果换短信服务商,只需要改这个组件的实现,不用全项目搜索替换。我见过一个项目把SendSmsRequest散落在十几个业务类里,后来要加公共逻辑,改起来非常痛苦。
第二,日志要记全。发送请求的签名、模板、手机号、返回码、返回消息、RequestId,这些信息务必打日志。否则出了问题,你连是哪一步失败的都不知道。特别是RequestId,反馈给阿里云工单时非常关键。
第三,测试环境建议不要用真实手机号狂发,可以用阿里云控制台提供的测试签名和测试模板,避免功能开发期就把生产签名、模板的频率限制打满。真实用户还没收到多少短信,测试就先把自己限流了,这种事真的很常见。
第四,如果你之后有语音通知需求,阿里云语音服务也在同一套账号体系下,接口模型和短信很像,把短信接口跑通了,语音接口的上手成本会低很多。
最后再分享一个我自己的习惯。每次接到新项目要接短信,我不会急着写代码,而是先打开控制台,把签名和模板申请提交上去,然后再回来写代码。通常代码写完了,审核也刚好通过,不耽误事。如果遇到审核驳回,也有充分的时间去补资料。这种“先出资源,再写业务”的顺序,在对接所有带审核环节的第三方服务时都适用。另外,接入之后我会主动跟团队成员讲一遍短信的计费方式和限流规则,因为这东西表面简单,但真被薅羊毛或者限流挡住业务时,代价可比写代码大得多。
本文还有配套的精品资源,点击获取