# 短信验证系统使用指南 **版本:** v1.0.0 **更新时间:** 2025-11-03 **系统名称:** 1818AI 用户服务短信验证模块 --- ## 📋 目录 - [系统概述](#系统概述) - [配置说明](#配置说明) - [API接口文档](#api接口文档) - [业务场景](#业务场景) - [安全机制](#安全机制) - [错误处理](#错误处理) - [使用示例](#使用示例) - [常见问题](#常见问题) - [维护指南](#维护指南) --- ## 📖 系统概述 ### 功能说明 短信验证系统基于**阿里云短信服务**实现,提供验证码的发送和校验功能。主要用于用户注册、登录、密码重置等关键业务场景。 ### 技术架构 ``` ┌─────────────────────────────────────────────────┐ │ 前端应用 │ └──────────────────┬──────────────────────────────┘ │ HTTP/HTTPS ▼ ┌─────────────────────────────────────────────────┐ │ Spring Boot 应用 │ │ ┌──────────────┐ ┌──────────────┐ │ │ │MsmController │─────▶│ MsmService │ │ │ └──────────────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │Redis缓存 │ │阿里云SMS API │ │ │ │(验证码存储) │ │(短信发送) │ │ │ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────┘ ``` ### 核心特性 - ✅ **6位数字验证码** - 简单易输入 - ✅ **5分钟有效期** - 自动过期保护 - ✅ **一次性使用** - 验证后立即失效 - ✅ **防重复发送** - 验证码存在时拒绝重发 - ✅ **强制发送模式** - 支持覆盖已存在的验证码 - ✅ **完整日志记录** - 便于追踪和调试 --- ## ⚙️ 配置说明 ### 配置文件位置 ``` src/main/resources/application.yml ``` ### 配置内容 ```yaml # --- 短信配置 --- ly: sms: accessKeyId: LTAI5t68do3qVXx5Rufugt3X # 阿里云AccessKey ID accessKeySecret: 2vD9ToIff49Vph4JQXsn0Cy8nXQfzA # 阿里云AccessKey Secret signName: 星洋智慧 # 短信签名 verifyTemplateCode: SMS_491985030 # 验证码短信模板编号 ``` ### 配置项说明 | 配置项 | 类型 | 必填 | 说明 | |--------|------|------|------| | `accessKeyId` | String | 是 | 阿里云访问密钥ID,从阿里云控制台获取 | | `accessKeySecret` | String | 是 | 阿里云访问密钥Secret,从阿里云控制台获取 | | `signName` | String | 是 | 短信签名,需在阿里云短信服务中申请 | | `verifyTemplateCode` | String | 是 | 短信模板编号,需在阿里云短信服务中申请 | ### 阿里云短信服务配置步骤 #### 1. 开通短信服务 1. 登录 [阿里云控制台](https://www.aliyun.com/) 2. 开通"短信服务"产品 3. 完成实名认证 #### 2. 创建短信签名 1. 进入"短信服务控制台" → "国内消息" → "签名管理" 2. 点击"添加签名" 3. 填写签名信息: - **签名名称**:星洋智慧(或您的公司/产品名) - **签名来源**:企事业单位的全称或简称 - **适用场景**:验证码 4. 提交审核(通常1-2个工作日) #### 3. 创建短信模板 1. 进入"模板管理" → "添加模板" 2. 填写模板信息: - **模板类型**:验证码 - **模板名称**:验证码通知 - **模板内容**:`您的验证码为:${code},5分钟内有效,请勿泄露给他人。` 3. 提交审核(通常1-2个工作日) 4. 审核通过后获得**模板CODE**(如:SMS_491985030) #### 4. 获取AccessKey 1. 进入"AccessKey管理" 2. 创建AccessKey(如果没有) 3. 记录 `AccessKey ID` 和 `AccessKey Secret` ⚠️ **安全提示**: - AccessKey Secret 请妥善保管,不要泄露 - 建议使用子账号AccessKey,并授予最小权限 - 定期轮换AccessKey --- ## 🔌 API接口文档 ### 1. 发送短信验证码 #### 接口信息 ``` GET /user/msm/send/{phone} ``` #### 请求参数 **路径参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | phone | String | 是 | 手机号(11位) | 13800138000 | **Query参数**: | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | force | Boolean | 否 | false | 是否强制发送新验证码 | #### 响应示例 **成功响应**: ```json { "code": 200, "message": "success", "data": true } ``` **失败响应**: ```json { "code": 400, "message": "验证码已存在,请稍后再试", "data": null } ``` ```json { "code": 500, "message": "短信发送失败,请稍后重试", "data": null } ``` #### 业务逻辑 ``` 1. 检查Redis中是否已有验证码 ├─ 有验证码 且 force=false → 返回错误(400) └─ 无验证码 或 force=true → 继续 ↓ 2. 生成6位随机数字验证码(100000-999999) ↓ 3. 调用阿里云短信API发送验证码 ↓ 4. 发送成功 ├─ 是 → 存入Redis(5分钟过期)→ 返回成功(200) └─ 否 → 返回错误(500) ``` #### 使用示例 **普通发送**: ```bash curl -X GET "http://localhost:8081/user/msm/send/13800138000" ``` **强制发送**(覆盖已存在的验证码): ```bash curl -X GET "http://localhost:8081/user/msm/send/13800138000?force=true" ``` #### 注意事项 - ⏱️ 验证码5分钟内有效 - 🔒 同一手机号在验证码未过期前不能重复发送(除非force=true) - 💰 每次发送会产生短信费用 - 📝 所有操作都会记录详细日志 --- ## 💼 业务场景 ### 1. 短信登录 **接口**:`POST /user/auth/sms-login` **流程**: ``` 用户输入手机号 ↓ 调用发送验证码接口 ↓ 用户收到短信 ↓ 用户输入验证码 ↓ 调用登录接口(验证码校验) ↓ 校验成功 → 生成JWT Token → 返回登录成功 ``` **请求示例**: ```json { "phone": "13800138000", "code": "123456" } ``` **响应示例**: ```json { "code": 200, "message": "success", "data": { "token": "eyJhbGciOiJIUzI1NiJ9...", "tokenExpiresAt": "2025-11-10T12:00:00", "userInfo": { "userId": 123456, "phone": "13800138000", "username": "用户昵称" } } } ``` ### 2. 用户注册 **接口**:`POST /user/auth/register` **流程**: ``` 用户输入手机号和密码 ↓ 调用发送验证码接口 ↓ 用户输入验证码 ↓ 调用注册接口(验证码校验) ↓ 校验成功 → 创建用户 → 自动登录 → 返回Token ``` **请求示例**: ```json { "phone": "13800138000", "code": "123456", "password": "Abc123456", "inviteCode": "ABC123" // 可选 } ``` ### 3. 重置密码 **接口**:`POST /user/auth/reset-password` **流程**: ``` 用户输入手机号 ↓ 调用发送验证码接口 ↓ 用户输入验证码和新密码 ↓ 调用重置密码接口(验证码校验) ↓ 校验成功 → 更新密码 → 返回成功 ``` **请求示例**: ```json { "phone": "13800138000", "code": "123456", "newPassword": "NewPass123" } ``` ### 4. 修改手机号 **接口**:`PUT /user/users/info` **流程**: ``` 用户输入新手机号 ↓ 向新手机号发送验证码 ↓ 用户输入验证码 ↓ 调用修改接口(验证码校验) ↓ 校验成功 → 更新手机号 → 返回成功 ``` ### 5. 修改密码(需要验证码) **接口**:`PUT /user/users/info` **流程**: ``` 用户输入当前手机号 ↓ 发送验证码 ↓ 用户输入验证码和新密码 ↓ 调用修改接口(验证码校验) ↓ 校验成功 → 更新密码 → 返回成功 ``` --- ## 🔒 安全机制 ### 1. 验证码有效期控制 ```java // 存储到Redis,5分钟后自动过期 redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES); ``` **说明**: - 验证码在Redis中存储5分钟 - 5分钟后自动删除,无法继续使用 - 防止验证码长期有效带来的安全风险 ### 2. 一次性使用机制 ```java // 验证成功后立即删除 redisTemplate.delete(phone); ``` **说明**: - 验证码验证成功后立即从Redis删除 - 每个验证码只能使用一次 - 防止验证码被重复使用 ### 3. 错误清除机制 ```java // 验证失败时清除验证码 if (!cachedCode.equals(code)) { redisTemplate.delete(phone); throw new RuntimeException("验证码错误或已过期"); } ``` **说明**: - 验证码输入错误时立即清除 - 防止暴力破解攻击 - 用户需重新获取验证码 ### 4. 业务失败清除 ```java // 业务逻辑失败时也清除验证码 if (existingUser != null) { redisTemplate.delete(phone); throw new RuntimeException("手机号已注册"); } ``` **说明**: - 即使验证码正确,但业务逻辑失败时也清除验证码 - 例如:注册时手机号已存在、登录时用户不存在等 - 确保一个验证码只用于一次完整的业务操作 ### 5. 重复发送限制 ```java // 检查是否已有验证码 String existingCode = redisTemplate.opsForValue().get(phone); if (existingCode != null && !force) { return Result.error(400, "验证码已存在,请稍后再试"); } ``` **说明**: - 验证码存在时拒绝重复发送 - 防止短信轰炸和资源浪费 - 特殊情况可使用`force=true`强制发送 ### 安全建议 #### ⚠️ 当前缺少的安全措施 1. **频率限制** ``` 建议:同一手机号1分钟内最多发送1次 建议:同一IP每小时最多发送10次 建议:单个手机号每天最多发送5次 ``` 2. **图形验证码** ``` 建议:发送短信前先验证图形验证码 防止:自动化机器人攻击 ``` 3. **手机号归属地验证** ``` 建议:检查手机号归属地是否为国内 防止:国际短信费用损失 ``` 4. **黑名单机制** ``` 建议:维护恶意手机号黑名单 防止:滥用和攻击 ``` --- ## ❌ 错误处理 ### 错误码说明 | 错误码 | 错误信息 | 原因 | 解决方法 | |--------|---------|------|----------| | 400 | 验证码已存在,请稍后再试 | Redis中已有未过期的验证码 | 等待5分钟或使用force=true | | 400 | 验证码错误或已过期 | 验证码不存在或不匹配 | 重新获取验证码 | | 500 | 短信发送失败,请稍后重试 | 阿里云短信服务调用失败 | 检查配置和网络,查看日志 | | 500 | 发送短信验证码失败 | 系统异常 | 查看服务器日志 | ### 阿里云短信服务错误码 | 阿里云错误码 | 说明 | 处理方法 | |-------------|------|----------| | OK | 发送成功 | - | | isv.MOBILE_NUMBER_ILLEGAL | 手机号格式错误 | 检查手机号格式 | | isv.BUSINESS_LIMIT_CONTROL | 业务限流 | 降低发送频率 | | isv.AMOUNT_NOT_ENOUGH | 账户余额不足 | 充值阿里云短信服务 | | isv.TEMPLATE_MISSING_PARAMETERS | 模板参数缺失 | 检查模板参数 | | isv.INVALID_PARAMETERS | 参数无效 | 检查所有参数 | ### 日志查询 ```bash # 查看短信发送日志 sudo journalctl -u spring_1818_user_server | grep "短信" # 查看特定手机号的日志 sudo journalctl -u spring_1818_user_server | grep "13800138000" # 查看错误日志 sudo journalctl -u spring_1818_user_server | grep -E "ERROR|WARN" | grep "短信" ``` --- ## 📘 使用示例 ### 完整的登录流程示例 #### 步骤1:发送验证码 ```bash curl -X GET "http://localhost:8081/user/msm/send/13800138000" \ -H "Accept: application/json" ``` **响应**: ```json { "code": 200, "message": "success", "data": true } ``` #### 步骤2:用户收到短信 ``` 【星洋智慧】您的验证码为:123456,5分钟内有效,请勿泄露给他人。 ``` #### 步骤3:使用验证码登录 ```bash curl -X POST "http://localhost:8081/user/auth/sms-login" \ -H "Content-Type: application/json" \ -d '{ "phone": "13800138000", "code": "123456" }' ``` **响应**: ```json { "code": 200, "message": "success", "data": { "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTYiLCJwaG9uZSI6IjEzODAwMTM4MDAwIiwiaWF0IjoxNjk5MDA4MDAwLCJleHAiOjE2OTk2MTI4MDB9.xxx", "tokenExpiresAt": "2025-11-10T12:00:00", "userInfo": { "userId": 123456, "phone": "13800138000", "username": "测试用户" } } } ``` ### 强制发送验证码示例 **场景**:用户点击"重新发送"时,即使验证码未过期也要发送新的 ```bash curl -X GET "http://localhost:8081/user/msm/send/13800138000?force=true" \ -H "Accept: application/json" ``` ### JavaScript/TypeScript 示例 ```typescript // 发送验证码 async function sendSmsCode(phone: string, force: boolean = false): Promise { try { const response = await fetch( `http://localhost:8081/user/msm/send/${phone}?force=${force}`, { method: 'GET', headers: { 'Accept': 'application/json' } } ); const result = await response.json(); if (result.code === 200) { console.log('验证码发送成功'); return true; } else { console.error('验证码发送失败:', result.message); return false; } } catch (error) { console.error('网络错误:', error); return false; } } // 短信登录 async function smsLogin(phone: string, code: string): Promise { const response = await fetch('http://localhost:8081/user/auth/sms-login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, code }) }); const result = await response.json(); if (result.code === 200) { // 保存token到localStorage localStorage.setItem('token', result.data.token); return result.data; } else { throw new Error(result.message); } } // 使用示例 async function handleLogin() { const phone = '13800138000'; const code = '123456'; try { const loginData = await smsLogin(phone, code); console.log('登录成功:', loginData); // 跳转到主页 window.location.href = '/home'; } catch (error) { console.error('登录失败:', error.message); alert(error.message); } } ``` --- ## ❓ 常见问题 ### Q1: 验证码收不到怎么办? **A**: 检查以下几点: 1. 手机号格式是否正确(11位数字) 2. 查看服务器日志,确认是否发送成功 3. 检查阿里云短信服务余额是否充足 4. 确认短信签名和模板是否已审核通过 5. 检查手机是否有信号,是否被拦截为垃圾短信 ### Q2: 提示"验证码已存在"怎么办? **A**: 两种解决方法: 1. 等待5分钟后重试(验证码自动过期) 2. 使用`force=true`参数强制发送新验证码 ```bash curl -X GET "http://localhost:8081/user/msm/send/13800138000?force=true" ``` ### Q3: 验证码输入错误后无法重试? **A**: 这是安全机制设计,验证码输入错误后会立即失效。需要重新获取新的验证码。 ### Q4: 如何查看短信发送记录? **A**: 查看服务器日志: ```bash # 查看所有短信相关日志 sudo journalctl -u spring_1818_user_server | grep "短信" # 查看特定手机号的发送记录 sudo journalctl -u spring_1818_user_server | grep "phone: 13800138000" ``` ### Q5: 短信费用如何计算? **A**: - 国内短信:约0.045元/条 - 计费由阿里云短信服务收取 - 可在阿里云控制台查看详细账单 ### Q6: 如何修改验证码有效期? **A**: 修改代码中的过期时间: ```java // 当前是5分钟 redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES); // 修改为3分钟 redisTemplate.opsForValue().set(phone, code, 3, TimeUnit.MINUTES); ``` ### Q7: 如何修改验证码位数? **A**: 修改生成验证码的代码: ```java // 当前是6位(100000-999999) String code = String.valueOf((int) ((Math.random() * 9 + 1) * 100000)); // 修改为4位(1000-9999) String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000)); ``` ### Q8: 如何添加发送频率限制? **A**: 需要在代码中添加额外的Redis计数器: ```java // 伪代码示例 String countKey = "sms:count:" + phone; Integer count = redisTemplate.opsForValue().get(countKey); if (count != null && count >= 5) { return Result.error(429, "发送次数过多,请明天再试"); } // 发送成功后增加计数 redisTemplate.opsForValue().increment(countKey); // 设置24小时过期 redisTemplate.expire(countKey, 24, TimeUnit.HOURS); ``` --- ## 🛠️ 维护指南 ### 监控指标 #### 1. 短信发送成功率 ```sql -- 查看今日短信发送情况(需要添加短信发送记录表) SELECT DATE(create_time) as date, COUNT(*) as total, SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as success, SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed, ROUND(SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as success_rate FROM sms_log WHERE DATE(create_time) = CURDATE() GROUP BY DATE(create_time); ``` #### 2. Redis验证码监控 ```bash # 连接Redis redis-cli # 查看所有手机号的验证码(生产环境不建议执行) KEYS * # 查看特定手机号的验证码 GET 13800138000 # 查看验证码剩余过期时间(秒) TTL 13800138000 ``` #### 3. 日志监控 ```bash # 统计今日短信发送次数 sudo journalctl -u spring_1818_user_server --since today | grep "短信验证码发送成功" | wc -l # 统计今日短信发送失败次数 sudo journalctl -u spring_1818_user_server --since today | grep "短信验证码发送失败" | wc -l # 查看最近的错误 sudo journalctl -u spring_1818_user_server -n 100 | grep -E "ERROR.*短信" ``` ### 定期检查项 #### 每日检查 - [ ] 查看短信发送成功率 - [ ] 检查阿里云短信服务余额 - [ ] 查看错误日志 #### 每周检查 - [ ] 分析短信发送量趋势 - [ ] 检查异常手机号(发送失败率高的) - [ ] 审查Redis使用情况 #### 每月检查 - [ ] 审查短信费用 - [ ] 更新AccessKey(建议定期轮换) - [ ] 检查短信签名和模板有效期 ### 故障处理 #### 故障1:大量短信发送失败 **排查步骤**: 1. 检查阿里云短信服务是否正常 2. 检查网络连接是否正常 3. 检查AccessKey是否过期 4. 检查账户余额是否充足 5. 查看详细错误日志 #### 故障2:Redis连接失败 **排查步骤**: 1. 检查Redis服务是否运行 2. 检查Redis连接配置 3. 检查防火墙设置 4. 重启Redis服务 #### 故障3:验证码无法验证 **排查步骤**: 1. 检查Redis中是否存在验证码 2. 检查验证码是否已过期 3. 检查代码逻辑是否正确 4. 查看详细日志 ### 性能优化建议 #### 1. Redis连接池优化 ```yaml spring: redis: lettuce: pool: max-active: 20 # 最大连接数 max-idle: 10 # 最大空闲连接数 min-idle: 5 # 最小空闲连接数 ``` #### 2. 短信发送异步化 ```java // 使用@Async异步发送短信 @Async public CompletableFuture sendAsync(Map param, String phone) { boolean result = send(param, phone); return CompletableFuture.completedFuture(result); } ``` #### 3. 添加缓存预热 ```java // 应用启动时检查Redis连接 @PostConstruct public void init() { try { redisTemplate.opsForValue().set("health_check", "ok", 10, TimeUnit.SECONDS); log.info("Redis连接正常"); } catch (Exception e) { log.error("Redis连接失败", e); } } ``` --- ## 📝 更新日志 ### v1.0.0 (2025-11-03) **初始版本**: - ✅ 实现基础短信验证码发送功能 - ✅ 集成阿里云短信服务 - ✅ 实现Redis验证码存储 - ✅ 实现5分钟过期机制 - ✅ 实现一次性使用机制 - ✅ 添加强制发送模式 - ✅ 完善日志记录 --- ## 📞 技术支持 **遇到问题?** 1. 查看本文档的[常见问题](#常见问题)章节 2. 查看服务器日志获取详细错误信息 3. 检查阿里云短信服务控制台 4. 联系技术团队 **相关文档**: - [阿里云短信服务文档](https://help.aliyun.com/document_detail/101414.html) - [Redis官方文档](https://redis.io/documentation) - [Spring Boot Redis文档](https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.nosql.redis) --- **文档版本**: v1.0.0 **最后更新**: 2025-11-03 **维护团队**: 1818AI技术团队