概述
交友项目从第一行业务代码开始就要发短信验证码,而发送短信这件事个人和中小团队都做不了------没有运营商资质。本文讲清项目项目为什么必须接第三方云服务、具体接了阿里云的哪几个、以及从注册账号到密钥落地的完整接入流程,顺带把 AccessKey 的安全红线说透。
纲要
- 云服务存在的意义:短信、对象存储、内容审核、人脸识别为什么不该自建
- 阿里云简介:它提供什么,本项目用的是哪一类服务
- 本项目实际接入的阿里云服务清单(短信 / OSS / 内容审核),以及一个容易搞错的点:人脸识别用的是百度
- 第三方服务全景表:跟着项目走下来,一共要注册哪些账号
- 开通与接入的通用流程:实名认证 → 开通服务 → RAM 子用户 → AccessKey → 配置落地
- 费用与免费额度:哪些按条、哪些按量,怎么防止意外扣费
- AccessKey 安全规范:为什么必须用 RAM 子账号、密钥该放哪、泄露后怎么办
- 工程实读:
mf-autoconfig的 pom 依赖与自动装配骨架 - 与后续章节的衔接映射
- 常见误解澄清
为什么项目要用云服务
先说结论:短信、对象存储(OSS)、内容审核、人脸识别这四类能力,自建的成本高到不现实。
| 能力 | 自建需要付出什么 | 用云服务的代价 |
|---|---|---|
| 短信 | 申请运营商通道、企业资质备案、签名与模板报备,个人几乎不可能拿到 | 按条付费,调一个 API 就发出去了 |
| 对象存储 | 买磁盘、维护分布式文件系统、做冗余和扩容、处理 CDN 回源 | 存多少算多少,按存储量和流量计费 |
| 内容审核 | 自建模型团队或采购算法,还要持续迭代模型对抗绕过 | 把文字/图片丢给 API,返回是否合规 |
| 人脸识别 | 同上,且涉及生物特征合规问题 | 上传图片拿比对分数 |
交友项目是学习型项目,重点在业务逻辑与架构(Dubbo 拆分、MongoDB 地理位置、Redis 缓存、Spark 推荐)。把时间花在搭短信网关或自研审核模型上,等于跑偏。云服务提供的是开箱即用 + 按量付费,这正是学习项目最需要的形态。
短信验证码这条链路最能说明问题------服务端不直接对接运营商,中间隔着第三方云服务:
运营商 阿里云短信 Java 服务端 手机端 运营商 阿里云短信 Java 服务端 手机端 #mermaid-svg-Bd2KCvVVsEeuHUQ7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .error-icon{fill:#552222;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .marker.cross{stroke:#333333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 p{margin:0;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .sequenceNumber{fill:white;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 #sequencenumber{fill:#333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .messageText{fill:#333;stroke:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .labelText,#mermaid-svg-Bd2KCvVVsEeuHUQ7 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .loopText,#mermaid-svg-Bd2KCvVVsEeuHUQ7 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .noteText,#mermaid-svg-Bd2KCvVVsEeuHUQ7 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actorPopupMenu{position:absolute;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 .actor-man circle,#mermaid-svg-Bd2KCvVVsEeuHUQ7 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Bd2KCvVVsEeuHUQ7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求发送验证码(手机号) 调用 SendSms API(AK 鉴权) 提交短信内容 回执 返回 Code/Message 下发短信 提交验证码校验
阿里云简介
阿里云是国内市场占有率领先的云厂商,产品线覆盖计算(ECS 云服务器)、存储(OSS 对象存储)、数据库(RDS)、中间件(消息队列、注册中心)、AI 能力(内容审核、人脸人体)和通信能力(短信服务)。
这里有个常见误解要先破掉:阿里云不等于"租服务器"。项目项目一台 ECS 都没买,用的是其中的 PaaS 与 API 类服务------SDK 引入依赖、填上密钥,服务端直接调接口。计算资源仍然跑在本地/自己的机器上。
本项目实际用到的阿里云服务
以工程实读为准。mf-autoconfig 的 pom 里出现的阿里云依赖,就是项目真正会用的服务:
| 服务名 | 用途 | 用在哪个功能 | 是否必须 |
|---|---|---|---|
短信服务 Dysmsapi(dysmsapi20170525) |
下发手机验证码 | 注册、登录的短信验证码 | 必须,第一章就要用 |
对象存储 OSS(aliyun-sdk-oss) |
存用户头像、动态图片、小视频封面 | 完善用户信息、发布动态/视频 | 必须 |
内容审核 Green(aliyun-java-sdk-green) |
动态文字与图片的合规审核 | 后台管理系统的审核功能 | 后续章节用到 |
核心 SDK(aliyun-java-sdk-core) |
上面若干 SDK 的公共依赖 | 全局 | 传递依赖 |
人脸人体 Facebody(facebody20191230) |
人脸检测(备用能力) | 见下方说明 | 可选 |
注意别搞混 :项目的人脸识别功能走的是百度 AI (com.baidu.aip:java-sdk),不是阿里云。pom 里那两个依赖都躺着,容易让人以为"人脸也是阿里云"。实际业务用的是百度人脸识别的解决方案,讲义 1.9 节写得很明确。至于 facebody20191230 这行,属于随大流引入的备用依赖。
把项目的第三方服务拉成一张全景表,注册账号前先看清楚要办哪几张:
| 服务 | 厂商 | 用途 | 对应章节 |
|---|---|---|---|
| 短信服务 | 阿里云 | 登录/注册验证码 | 短信验证码 |
| 对象存储 OSS | 阿里云 | 头像、动态图片存储 | 完善用户信息 |
| 内容审核 | 阿里云 | 动态文字/图片审核 | 后台系统 |
| 人脸识别 | 百度 AI | 人脸检测与比对 | 用户信息/动态 |
| 即时通讯 IM | 环信 | 聊天消息 | 消息模块 |
开通与接入的通用流程
不管接哪个云服务,步骤都是同一套骨架。以阿里云短信为例:
#mermaid-svg-KA8v7Bm9Xn0H6WGh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .error-icon{fill:#552222;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .marker.cross{stroke:#333333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh p{margin:0;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster-label text{fill:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster-label span{color:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster-label span p{background-color:transparent;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .label text,#mermaid-svg-KA8v7Bm9Xn0H6WGh span{fill:#333;color:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .node rect,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node circle,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node ellipse,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node polygon,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .rough-node .label text,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node .label text,#mermaid-svg-KA8v7Bm9Xn0H6WGh .image-shape .label,#mermaid-svg-KA8v7Bm9Xn0H6WGh .icon-shape .label{text-anchor:middle;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .rough-node .label,#mermaid-svg-KA8v7Bm9Xn0H6WGh .node .label,#mermaid-svg-KA8v7Bm9Xn0H6WGh .image-shape .label,#mermaid-svg-KA8v7Bm9Xn0H6WGh .icon-shape .label{text-align:center;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .node.clickable{cursor:pointer;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .arrowheadPath{fill:#333333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KA8v7Bm9Xn0H6WGh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KA8v7Bm9Xn0H6WGh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster text{fill:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .cluster span{color:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KA8v7Bm9Xn0H6WGh rect.text{fill:none;stroke-width:0;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .icon-shape,#mermaid-svg-KA8v7Bm9Xn0H6WGh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .icon-shape p,#mermaid-svg-KA8v7Bm9Xn0H6WGh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .icon-shape .label rect,#mermaid-svg-KA8v7Bm9Xn0H6WGh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KA8v7Bm9Xn0H6WGh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KA8v7Bm9Xn0H6WGh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KA8v7Bm9Xn0H6WGh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 注册阿里云账号
实名认证 - 个人认证即可
控制台搜索服务名并开通
申请签名与模板 - 短信服务人工审核
创建 RAM 子用户
只授予该服务权限
创建 AccessKeyId / AccessKeySecret
写入应用配置 - 环境变量 / yml / 配置中心
用官方示例代码测试连通性
各环节的关键动作与常见坑:
| 开通环节 | 关键动作 | 常见坑 |
|---|---|---|
| 注册与实名 | 用支付宝/手机号注册,完成个人实名认证 | 以为必须企业认证;个人认证可用,只是部分服务受限 |
| 开通服务 | 控制台顶部搜索框搜服务名 → 进入产品页 → 开通 | 开通了但没买短信包,依然能按条计费,别以为没开通 |
| 申请签名 | 短信 → 国内消息 → 签名管理 → 添加签名 | 签名要审核,测试可直接用官方提供的示例签名 |
| 申请模板 | 模板管理 → 添加模板,验证码类模板内容形如 您的验证码为${code} |
模板里的 ${code} 就是占位符,参数名必须与代码里的 JSON key 一致 |
| 创建 RAM 子用户 | 访问控制 RAM → 用户 → 创建用户 | 不要用主账号 AccessKey,权限过大且无法收窄 |
| 获取 AccessKey | RAM 用户详情 → 创建 AccessKey | AK Secret 只在创建时展示一次,没存下来只能删掉重建 |
| 配置落地 | 写入 yml / 环境变量 / 配置中心 | 直接硬编码进代码或提交到仓库 |
签名与模板两个环节最容易被忽略------很多同学以为开通服务就能发短信,实际代码跑起来报 isv.SMS_SIGNATURE_ILLEGAL 或 isv.SMS_TEMPLATE_ILLEGAL,就是签名/模板没通过或名字写错。
费用与免费额度
| 项目 | 计费方式 | 学习项目的量级 |
|---|---|---|
| 短信 | 按条计费,通知/验证码类单价较低 | 几十到几百条级别,成本可忽略(具体单价以官方定价为准) |
| OSS | 按存储容量 + 外网下行流量 + 请求次数 | 几张头像、几段小视频,几毛钱量级 |
| 内容审核 | 按调用次数计费 | 测试调用次数极少 |
| 人脸识别(百度) | 按调用次数,有免费额度 | 同样可控 |
新用户通常能拿到一定的免费额度或体验包。即便如此,也建议在费用中心设置余额预警和消费预警------不是防大额扣费,而是防"忘了自己开通过什么",尤其是绑了自动续费或者被脚本刷接口的情况。
AccessKey 安全规范
这是本篇最值得记住的一节。AccessKey 是一对身份凭证,谁拿到谁就能以你的身份调用阿里云 API,能开机器、能删数据、能发短信。主账号的 AK 权限等于全部权限。
必须用 RAM 子账号,理由有三个:
- 最小权限:只给这个子用户短信服务的权限,就算泄露也没法碰 OSS 和 ECS
- 可单独吊销:出事禁用子用户即可,不影响主账号
- 可审计:每个子用户的操作在日志里分得清
密钥放哪,优先级从上到下:
text
环境变量(CI/CD 注入) > 外部配置文件(不进仓库) > 配置中心 >> 代码里硬编码
硬编码是最差的做法。讲义里为了方便演示,直接把 AK 写进了 yml:
yaml
mf:
sms:
signName: 物流云商
templateCode: SMS_106590012
accessKey: <你的 AccessKeyId>
secret: <你的 AccessKeySecret>
这段在教学环境没问题,但绝对不能原样提交到 Git 仓库。正确做法是本地 yml 不纳入版本控制,或改用环境变量占位:
yaml
mf:
sms:
signName: ${SMS_SIGN_NAME}
templateCode: ${SMS_TEMPLATE_CODE}
accessKey: ${ALIYUN_ACCESS_KEY_ID}
secret: ${ALIYUN_ACCESS_KEY_SECRET}
泄露后的应急处置顺序:先在 RAM 控制台禁用该 AK(或直接禁用子用户)→ 生成新的 AK → 更新应用配置 → 到操作审计里排查是否已被调用过、调了什么。别先改代码,先断掉凭证,否则改代码的速度赶不上对方刷接口的速度。
团队协作时不要几个人共用一个 AK。出了事不知道是谁干的,也没法单独吊销某个人。一人一个 RAM 子用户,成本几乎为零。
工程实读:mf-autoconfig
项目没有在每个模块里重复写短信调用逻辑,而是把云服务的调用封装成一个可复用的自动装配模块 mf-autoconfig。目录结构如下:
text
mf-autoconfig
├── pom.xml # 阿里云短信/OSS/内容审核、百度人脸、环信 SDK
└── src/main
├── java/com/mf/autoconfig
│ ├── mfAutoConfiguration.java # 自动装配入口,注册各类 Template 到容器
│ ├── properties
│ │ ├── SmsProperties.java # @ConfigurationProperties(prefix = "mf.sms")
│ │ ├── OssProperties.java
│ │ └── GreenProperties.java
│ └── template
│ ├── SmsTemplate.java # 封装短信发送
│ ├── OssTemplate.java
│ └── GreenTemplate.java
└── resources/META-INF
└── spring.factories # 声明 mfAutoConfiguration
spring.factories 是这套机制的关键,它让其他模块只要引入依赖就自动获得这些能力:
text
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mf.autoconfig.mfAutoConfiguration
第一章只落了短信一个 Template,骨架是这样:
java
package com.mf.autoconfig;
import com.mf.autoconfig.properties.SmsProperties;
import com.mf.autoconfig.template.SmsTemplate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({
SmsProperties.class
})
public class mfAutoConfiguration {
@Bean
public SmsTemplate smsTemplate(SmsProperties properties) {
return new SmsTemplate(properties);
}
}
属性类把 yml 里的密钥、签名、模板号绑成对象:
java
package com.mf.autoconfig.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "mf.sms")
public class SmsProperties {
private String signName;
private String templateCode;
private String accessKey;
private String secret;
}
短信模板类里真正干活的是两件事:用 AccessKey 构造阿里云客户端,然后拼请求参数发出去:
java
package com.mf.autoconfig.template;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.mf.autoconfig.properties.SmsProperties;
public class SmsTemplate {
private SmsProperties properties;
public SmsTemplate(SmsProperties properties) {
this.properties = properties;
}
public void sendSms(String mobile, String code) {
try {
Config config = new Config()
.setAccessKeyId(properties.getAccessKey())
.setAccessKeySecret(properties.getSecret());
// 短信服务的接入域名
config.endpoint = "dysmsapi.aliyuncs.com";
com.aliyun.dysmsapi20170525.Client client =
new com.aliyun.dysmsapi20170525.Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(mobile)
.setSignName(properties.getSignName())
.setTemplateCode(properties.getTemplateCode())
.setTemplateParam("{\"code\":\"" + code + "\"}");
SendSmsResponse response = client.sendSms(sendSmsRequest);
SendSmsResponseBody body = response.getBody();
System.out.println(body.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
两个实读细节值得留意:一是讲义里写的是包名 com.mf.autoconfig.templates.SmsTemplate(复数),工程里实际是 template(单数),照抄讲义会编译不过;二是 templateParam 传的是 JSON 字符串,key 必须与模板里 ${code} 的占位名一致,否则短信能发出去但内容是空的。
与后续章节的衔接
| 云服务 | 在哪一章落地 |
|---|---|
| 阿里云短信 | 下一篇「阿里云短信入门案例」先跑通调用,再到「封装短信服务组件」抽成 SmsTemplate |
| 阿里云 OSS | 完善用户信息章节,上传头像 |
| 内容审核 Green | 后台系统章节,动态文字与图片审核 |
| 百度人脸识别 | 用户信息相关章节 |
常见误解澄清
- "用阿里云就得买服务器":不对。本项目用的全是 API/PaaS 类服务,不需要 ECS。
- "主账号 AccessKey 更方便":方便一秒,风险无限。主账号 AK 泄露等于账号被接管,一律用 RAM 子用户。
- "开通服务就能发短信":还差签名和模板,两个都要审核通过。
- "第三方能力都是阿里云的":人脸识别是百度的,即时通讯是环信的。
- "不买短信包就发不了":可以,按条计费;买套餐包只是更便宜。
官方文档
总结
交友项目用云服务,不是赶时髦,而是因为短信资质、分布式存储、审核算法这些能力自建不划算。项目实际接了三样阿里云能力------短信、OSS、内容审核,人脸识别是百度的,即时通讯是环信的,别记混。接入动作本身很简单,真正的门槛在开通流程(实名、签名、模板审核)和密钥管理(RAM 子用户 + 不硬编码)。把 mf-autoconfig 这个模块理解透,后面所有第三方能力都是同一套自动装配的套路。