登录注册、手机号、邮箱
This commit is contained in:
264
schoolNewsServ/auth/短信服务架构说明.md
Normal file
264
schoolNewsServ/auth/短信服务架构说明.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# 短信服务架构说明
|
||||
|
||||
## 设计理念
|
||||
|
||||
短信服务采用**通用化、可扩展**的设计:
|
||||
- ✅ 配置保持通用,不绑定特定服务商
|
||||
- ✅ 支持多种短信服务商切换
|
||||
- ✅ 易于扩展新的服务商
|
||||
- ✅ 统一的对外接口
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 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步
|
||||
|
||||
这样的设计既满足当前需求,又具备良好的扩展性,是一个**优雅、实用、易维护**的架构方案。
|
||||
|
||||
Reference in New Issue
Block a user