Spring Boot 整合阿里云短信(SMS)对接文档
本文档涵盖阿里云侧资质/签名/模板申请流程与后端整合、调用方式。
⚠️ 文档中 AccessKey ID / AccessKey Secret 等敏感信息已脱敏,真实值请以代码或配置中心为准。
一、整体说明
| 项 | 说明 |
|---|---|
| 云服务 | 阿里云短信服务(Dysmsapi) |
| SDK | aliyun-java-sdk-core+aliyun-java-sdk-dysmsapi(v1 版 SDK) |
| 核心接口 | SendSms(发送短信) |
| 接入点 | cn-hangzhou/dysmsapi.aliyuncs.com |
| 工具类 | com.jxbd.fsgdzc.util.AliyunSms#sendSms |
| 发送方式 | 通过status参数映射到不同短信模板,模板变量为 JSON(code/orderNo/name) |
调用链路:
业务代码(Service/Controller/Task) │ Map<String,String> param(telNo / status / code / orderNo / name) ▼ AliyunSms.sendSms(param, null) │ 1. 按 status 选择模板 ID │ 2. 组装模板变量 JSON │ 3. DefaultAcsClient.getAcsResponse(SendSmsRequest) ▼ 阿里云短信服务 → 运营商 → 用户手机二、阿里云侧开通与申请流程
2.1 开通短信服务
- 登录 阿里云控制台,完成企业实名认证(短信服务面向企业用户为主)。
- 搜索「短信服务」并开通(免费开通,按发送量计费)。
- 首次进入「短信服务控制台」会引导完成资质 + 签名 + 模板三件套,全部审核通过后才能发送。
经验值:资质审核一般 1~2 个工作日;签名/模板审核一般2 小时内(工作日),被驳回需按驳回原因修改后重新提交。
2.2 申请资质(短信资质)
阿里云自 2023 年起要求「先提交资质,再申请签名/模板」。
- 进入「短信服务控制台 → 国内消息 → 资质管理 → 新增资质」。
- 选择资质类型:
- 企业资质:需上传营业执照、法人身份证正反面、经办人身份证(非法人办理时需授权书)。
- 个人资质:仅可申请自用类签名/模板,业务范围受限。
- 填写企业名称、统一社会信用代码、归属行业等,提交审核。
- 审核通过后,资质状态变为「审核通过」,方可继续申请签名。
材料准备清单(企业)
| 材料 | 要求 |
|---|---|
| 营业执照 | 彩色扫描件/照片,清晰可辨且在有效期内 |
| 法人身份证 | 正反面,四角完整 |
| 经办人身份证 + 授权书 | 仅当经办人非法人时需要 |
| 行业证明(部分行业) | 医疗、金融等需提供对应经营许可 |
2.3 申请签名
短信签名是短信开头【】中的内容,例如本项目使用的【友医伴健康】。
- 进入「短信服务控制台 → 国内消息 → 签名管理 → 添加签名」。
- 填写:
- 签名名称:建议 2~12 个字,与 App/公众号/网站/营业执照名称相关。
- 签名来源:App(需提供应用商店链接 + 应用截图)/ 公众号 / 小程序 / 网站 / 企业全称简称。
- 适用场景:验证码 / 短信通知 / 推广短信(通知类与营销类需分开申请)。
- 关联资质:选择 2.2 中审核通过的资质。
- 提交后等待审核,通过后状态为「审核通过」。
- 记录签名名称,配置到代码
request.setSignName("...")。
注意:签名必须与业务主体一致,否则易被驳回;一个签名可被多个模板复用。
2.4 新增模板
进入「短信服务控制台 → 国内消息 → 模板管理 → 添加模板」。
选择模板类型:
- 验证码:
您的验证码为${code},5分钟内有效。 - 短信通知:如订单支付成功、订单指派等。
- 推广短信:营销类,需单独申请签名。
- 验证码:
填写模板内容,变量使用
${变量名}占位,例如:尊敬的客户,您的订单${orderNo}已支付成功,我们将尽快为您安排陪诊服务。填写变量说明(说明每个变量的含义与示例值)。
提交审核,通过后获得模板 CODE(形如
SMS_508125195)。将模板 CODE 配置到代码的
status → templateCode映射中。
模板申请注意事项
- 变量个数与顺序必须与代码里
templateParam的 key 完全对应(本项目为code/orderNo/name)。 - 模板中数字变量建议不要放在短信开头,避免被运营商判定为垃圾短信。
- 验证码类模板必须包含验证码 + 有效期说明。
- 同一业务内容不要重复提交多个相似模板,会被驳回。
2.5 创建 AccessKey(RAM 子账号)
不要使用主账号 AccessKey,建议创建 RAM 用户并授予最小权限。
- 进入「RAM 访问控制 → 用户 → 创建用户」,勾选「OpenAPI 调用访问」。
- 创建后保存AccessKey ID与AccessKey Secret(Secret 仅创建时展示一次)。
- 为该用户授权:系统策略
AliyunDysmsFullAccess(或自定义仅含dysms:SendSms的策略)。 - 将 AccessKey 配置到项目中(推荐通过配置中心/环境变量注入,而非硬编码)。
2.6 计费与配额
- 按发送条数计费,可购买短信套餐包降低成本。
- 默认有流控限制(如同一手机号验证码类 1 分钟内 1 条、1 小时内 5 条等),可在控制台申请调整。
- 建议在控制台开启「发送量预警」,避免异常刷量。
三、后端整合
3.1 引入依赖
server/jxbd-fsgdzc/pom.xml:
<!-- 阿里云 SDK 核心 --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.3.1</version></dependency><!-- 阿里云短信 SDK --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.0.0</version></dependency>3.2 配置项(建议改造为可配置)
当前实现将 AccessKey 与模板 ID硬编码在AliyunSms中,不便于多环境切换与密钥轮换。建议改为读取配置:
# application-dev.ymlaliyun:sms:access-key-id:"LTAI****************"# RAM 用户 AccessKeyIdaccess-key-secret:"************************"# RAM 用户 AccessKeySecretregion-id:"cn-hangzhou"endpoint:"dysmsapi.aliyuncs.com"sign-name:"友医伴健康"# 短信签名templates:captcha:"SMS_508015266"# 0 验证码paid:"SMS_508125195"# 1 下单支付成功assigned:"SMS_508160180"# 2 订单指派给陪诊师back:"SMS_507950246"# 3 订单回退通知管理员canceled:"SMS_507930223"# 4 订单取消通知管理员和陪诊师remindToday:"SMS_508015231"# 5 开始陪诊前提前一天通知就诊人auditPass:"SMS_508180014"# 6 陪诊师资格审核通过通知用户timeoutUser:"SMS_509425064"# 7 订单超时15分钟通知陪诊师timeoutCustomer:"SMS_509740055"# 8 订单超时通知客户购买超时服务后续新增模板时,只需在配置中增加一项 + 在映射中注册,无需改动业务代码。
3.3 工具类源码
路径:server/jxbd-fsgdzc/src/main/java/com/jxbd/fsgdzc/util/AliyunSms.java(AccessKey 已脱敏)
packagecom.jxbd.fsgdzc.util;importcom.alibaba.fastjson.JSONObject;importcom.aliyuncs.DefaultAcsClient;importcom.aliyuncs.IAcsClient;importcom.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;importcom.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;importcom.aliyuncs.exceptions.ClientException;importcom.aliyuncs.profile.DefaultProfile;importcom.aliyuncs.profile.IClientProfile;importcom.jxbd.common.core.domain.AjaxResult;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjavax.servlet.http.HttpSession;importjava.util.Map;publicclassAliyunSms{privatestaticfinalLoggerlog=LoggerFactory.getLogger(AliyunSms.class);staticfinalStringproduct="Dysmsapi";staticfinalStringdomain="dysmsapi.aliyuncs.com";staticfinalStringaccessKeyId="LTAI****************";// 已脱敏staticfinalStringaccessKeySecret="************************";// 已脱敏publicAliyunSms(){}publicstaticAjaxResultsendSms(Map<String,String>params,HttpSessionsession)throwsClientException{Stringtel=(String)params.get("telNo");Stringcode=(String)params.get("code");StringorderNo=(String)params.get("orderNo");Stringname=(String)params.get("name");System.setProperty("sun.net.client.defaultConnectTimeout","10000");System.setProperty("sun.net.client.defaultReadTimeout","10000");IClientProfileprofile=DefaultProfile.getProfile("cn-hangzhou",accessKeyId,accessKeySecret);DefaultProfile.addEndpoint("cn-hangzhou","cn-hangzhou",product,domain);IAcsClientacsClient=newDefaultAcsClient(profile);SendSmsRequestrequest=newSendSmsRequest();request.setPhoneNumbers(tel);request.setSignName("友医伴健康");Stringstatus=(String)params.get("status");Stringtemplate=null;if("0".equals(status)){template="SMS_508015266";//验证码}elseif("1".equals(status)){template="SMS_508125195";//下单支付成功}elseif("2".equals(status)){// template = "SMS_508030056";//订单指派给陪诊师template="SMS_508160180";//订单指派给陪诊师}elseif("3".equals(status)){template="SMS_507950246";//订单回退通知管理员}elseif("4".equals(status)){template="SMS_507930223";//订单取消通知管理员和陪诊师}elseif("5".equals(status)){template="SMS_508015231";//开始陪诊前提前一天通知就诊人}elseif("6".equals(status)){template="SMS_508180014";//陪诊师资格审核通过后通知用户}elseif("7".equals(status)){template="SMS_509425064";//订单超时15分钟通知陪诊师(让客户购买延时服务)}elseif("8".equals(status)){template="SMS_509740055";//订单超时通知客户购买超时服务}request.setTemplateCode(template);log.info("模板id为:"+template);JSONObjectobj=newJSONObject();obj.put("code",code);obj.put("orderNo",orderNo);obj.put("name",name);request.setTemplateParam(obj.toJSONString());try{SendSmsResponsesendSmsResponse=(SendSmsResponse)acsClient.getAcsResponse(request);log.info("短信接口返回的数据--------start--------");log.info("Code="+sendSmsResponse.getCode());log.info("Message="+sendSmsResponse.getMessage());log.info("RequestId="+sendSmsResponse.getRequestId());log.info("BizId="+sendSmsResponse.getBizId());log.info("短信接口返回的数据-------- end --------");}catch(ClientExceptionvar14){log.error(var14.getMessage());var14.printStackTrace();}returnAjaxResult.success();}}3.4 调用示例
以「订单指派给陪诊师后发送通知」为例(这里最好改成异步执行,防止阻塞主流程,影响性能与用户体验,并且在阿里云短信控制台设置流控限制):
// 发送短信 TODOSysUsersysUser=sysUserService.selectUserById(assignUserId);Map<String,String>param=newHashMap<>();param.put("telNo",sysUser.getPhonenumber());// param.put("name", sysUser.getNickName());param.put("orderNo",tOrder.getOrderNo());//指派订单模板param.put("status","2");//调用阿里云发送短信try{AliyunSms.sendSms(param,null);}catch(ClientExceptione){log.error("发送短信出错:"+e.getMessage());e.printStackTrace();}参数约定
| key | 是否必填 | 说明 |
|---|---|---|
telNo | 必填 | 接收手机号,多个号码用英文逗号分隔(单次最多 1000 个) |
status | 必填 | 业务场景编码,决定使用哪个模板(见 4.1) |
code | 场景相关 | 验证码,仅status=0使用 |
orderNo | 场景相关 | 订单号 |
name | 场景相关 | 姓名(当前部分场景已注释) |
只有当前模板里声明过的变量才会生效:工具类固定提交
code/orderNo/name三个变量,多余变量不影响发送,缺失变量会导致发送失败。
四、模板对照与调用点
4.1status→ 模板对照表
| status | 模板 CODE | 用途 | 使用的变量 |
|---|---|---|---|
0 | SMS_508015266 | 验证码 | code |
1 | SMS_508125195 | 下单支付成功 | orderNo |
2 | SMS_508160180 | 订单指派给陪诊师 | orderNo |
3 | SMS_507950246 | 订单回退通知管理员 | orderNo |
4 | SMS_507930223 | 订单取消通知管理员和陪诊师 | orderNo |
5 | SMS_508015231 | 开始陪诊前提前一天通知就诊人 | orderNo |
6 | SMS_508180014 | 陪诊师资格审核通过后通知用户 | name |
7 | SMS_509425064 | 订单超时 15 分钟通知陪诊师 | orderNo |
8 | SMS_509740055 | 订单超时通知客户购买超时服务 | orderNo |
历史模板
SMS_508030056(订单指派)已废弃,改用SMS_508160180。
4.2 调用点清单
| 场景 | status | 位置 |
|---|---|---|
| 小程序登录验证码 | 0 | service/impl/IJxMpWxServiceImpl.java |
| 订单支付成功 | 1 | wxpay/service/WxPayServiceImpl.java(微信支付回调成功后) |
| 订单指派 / 回退 | 2/3 | service/impl/TOrderServiceImpl.java |
| 订单取消 | 4 | service/impl/TOrderServiceImpl.java |
| 陪诊前一日提醒 | 5 | service/impl/TOrderServiceImpl.java |
| 陪诊师资格审核通过 | 6 | controller/TMedicalApplicationController.java |
| 订单超时通知陪诊师 | 7 | service/impl/TOrderServiceImpl.java(定时任务) |
| 订单超时通知客户 | 8 | service/impl/TOrderServiceImpl.java(定时任务) |
五、常见错误码
| 返回 Code | 说明 | 处理建议 |
|---|---|---|
OK | 发送成功 | — |
isv.SMS_SIGNATURE_ILLEGAL | 签名不合法 / 未审核通过 | 检查SignName与签名状态 |
isv.SMS_TEMPLATE_ILLEGAL | 模板不合法 / 未审核通过 | 检查模板 CODE 与状态 |
isv.MOBILE_NUMBER_ILLEGAL | 手机号格式错误 | 校验手机号 |
isv.AMOUNT_NOT_ENOUGH | 账户余额不足 | 充值 / 购买套餐包 |
isv.BUSINESS_LIMIT_CONTROL | 触发流控 | 降低频率或申请提额 |
isv.OUT_OF_SERVICE | 业务停机(欠费) | 检查账户状态 |
isv.PARAM_LENGTH_LIMIT/isv.INVALID_PARAMETERS | 参数不合法(如变量缺失/超长) | 核对模板变量 |
SignatureDoesNotMatch | AccessKey 错误 | 检查 AK/SK 配置 |
排查时优先查看日志中的
Code与Message,并在控制台「发送记录」中核对实际下发结果。