Files
schoolNews/schoolNewsServ/auth/短信服务架构说明.md

265 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 短信服务架构说明
## 设计理念
短信服务采用**通用化、可扩展**的设计:
- ✅ 配置保持通用,不绑定特定服务商
- ✅ 支持多种短信服务商切换
- ✅ 易于扩展新的服务商
- ✅ 统一的对外接口
## 架构设计
### 1. 配置层(通用化)
```yaml
sms:
enabled: false # 是否启用
provider: aliyun # 服务商选择
access-key-id: xxx # 通用配置
access-key-secret: xxx
sign-name: xxx
template-code: xxx
region-id: xxx
```
**设计优势**
- 配置不带服务商前缀,保持通用性
- 通过 `provider` 灵活切换服务商
- 未来增加服务商无需修改配置结构
### 2. 工具类层SmsUtils
```java
@Component
public class SmsUtils {
// 通用配置
@Value("${sms.provider:aliyun}")
private String provider;
// 对外统一接口
public boolean sendVerificationCode(String phone, String code) {
switch (provider) {
case "aliyun":
return sendByAliyun(phone, code);
case "tencent":
return sendByTencent(phone, code);
default:
return false;
}
}
// 各服务商的私有实现
private boolean sendByAliyun(String phone, String code) { ... }
private boolean sendByTencent(String phone, String code) { ... }
}
```
**设计优势**
- 对外统一接口:`sendVerificationCode()`
- 各服务商只是内部的私有方法
- 调用方无需关心使用哪个服务商
- 新增服务商只需添加一个私有方法
### 3. 调用层AuthController
```java
@Autowired
private SmsUtils smsUtils;
public ResultDomain<Boolean> sendSmsCode(...) {
// 直接调用统一接口,无需关心服务商
boolean success = smsUtils.sendVerificationCode(phone, code);
...
}
```
## 支持的服务商
### 当前已实现
-**阿里云**dysmsapi20170525
### 待实现
-**腾讯云**(预留接口)
-**华为云**(预留接口)
## 如何添加新服务商
### 步骤1添加Maven依赖
`common-util/pom.xml` 中添加对应SDK
```xml
<!-- 例如:腾讯云短信 -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.x.x</version>
</dependency>
```
### 步骤2实现私有方法
`SmsUtils.java` 中添加私有实现方法:
```java
/**
* 使用腾讯云发送短信验证码
*/
private boolean sendByTencent(String phone, String code) {
try {
// 腾讯云SDK调用逻辑
...
return true;
} catch (Exception e) {
logger.error("腾讯云短信发送失败", e);
return false;
}
}
```
### 步骤3添加到分发逻辑
`sendVerificationCode()` 方法中添加case
```java
switch (provider.toLowerCase()) {
case "aliyun":
return sendByAliyun(phone, code);
case "tencent":
return sendByTencent(phone, code); // 新增
default:
logger.error("未知的短信服务商: {}", provider);
return false;
}
```
### 步骤4配置切换
修改 `application.yml`
```yaml
sms:
provider: tencent # 切换到腾讯云
```
完成!无需修改调用方代码。
## 开发模式
### 模拟模式(推荐用于开发)
```yaml
sms:
enabled: false # 关闭真实发送
```
**特点**
- 不会实际发送短信
- 验证码输出到日志
- 不产生任何费用
- 便于本地调试
### 真实发送模式
```yaml
sms:
enabled: true
provider: aliyun
access-key-id: xxx
access-key-secret: xxx
```
## 最佳实践
### 1. 环境隔离
**开发环境**
```yaml
sms:
enabled: false # 模拟模式
```
**生产环境**
```yaml
sms:
enabled: true
provider: aliyun
access-key-id: ${SMS_ACCESS_KEY_ID} # 从环境变量读取
access-key-secret: ${SMS_ACCESS_KEY_SECRET}
```
### 2. 安全性
- ❌ 不要将 AccessKey 写在代码中
- ❌ 不要将 AccessKey 提交到 Git
- ✅ 使用环境变量或配置中心
- ✅ 使用 RAM 子账号而非主账号
- ✅ 定期更换密钥
### 3. 可靠性
```java
// 系统已实现:
- 60秒发送频率限制防刷
- 10分钟验证码有效期
- Redis存储验证码
- 手机号格式验证
// 建议增加:
- 图形验证码前置
- IP限流
- 黑名单机制
- 发送失败重试
```
### 4. 监控告警
建议监控指标:
- 短信发送成功率
- 短信发送量(防异常消耗)
- 验证码验证成功率
- 单个手机号发送频率
## 优势总结
### 对比旧方案
**旧方案**(绑定服务商):
```yaml
aliyun:
sms:
enabled: true
```
- ❌ 配置绑定服务商
- ❌ 切换服务商需要大量修改
- ❌ 扩展性差
**新方案**(通用化):
```yaml
sms:
provider: aliyun
enabled: true
```
- ✅ 配置保持通用
- ✅ 切换服务商只需修改 `provider`
- ✅ 扩展性强,易于维护
### 符合设计原则
- **开闭原则**:对扩展开放,对修改关闭
- **单一职责**:每个服务商实现独立
- **依赖倒置**:调用方依赖抽象接口,不依赖具体实现
- **里氏替换**:各服务商实现可以互相替换
## 总结
这个架构设计的核心理念是:
1. **配置通用化**:不绑定特定服务商
2. **实现私有化**:服务商只是一个方法
3. **接口统一化**:对外提供统一接口
4. **扩展简单化**新增服务商仅需3步
这样的设计既满足当前需求,又具备良好的扩展性,是一个**优雅、实用、易维护**的架构方案。