概述
交友项目第一章从零把一个散装的 Maven 空工程,搭成了一个能注册到 Nacos、能发短信、能用手机号 + 验证码登录的微服务骨架。本篇不复述每一步怎么点,而是把这一章的五个板块串成一条链,抽走那些"下次还会再写一遍"的套路,并给出一份能自己勾选的自检清单。
纲要
- 本章一句话产出:可运行的微服务骨架 + 手机号验证码登录(含首次登录自动注册)
- 本章知识地图:五个板块的依赖顺序(前一板块是后一板块的前提)
- 工程终态:第一章结束时的真实模块清单与关键类
- 可复用套路清单:第三方能力封装四件套 / Dubbo 三件套 / 验证码 + Redis / JWT / BasePojo
- 关键坑回顾:
spring.factories路径、Dubbo 注册协议、序列化、123456硬编码 - 自检清单:八项可勾选验证
- 练习建议:唯一索引 + 频控 + 密钥外置
- 下一章预告:完善用户信息(头像、人脸识别、统一鉴权)
本章一句话产出
一句话概括:用 Dubbo + Nacos 搭起可注册的微服务骨架,跑通"手机号 + 验证码"登录,新用户在登录时自动注册,并把时间字段的填充收敛到公共基类。
这句话里有三个动作,缺一个链路就断:骨架要先注册得上去,短信才发得出去;验证码要能落进 Redis,登录时才有东西校验;用户表要能查、能写,自动注册才成立。
本章知识地图
五个板块不是并列的,是有严格先后依赖的。前一板块不成立,后一板块就没有落脚点。
依赖关系说穿了就三句话:
- 模块划分不定好,
mf-autoconfig这个组件就没地方放,mf-dubbo-interface也不能被别的模块依赖。 - 依赖没在父工程
dependencyManagement里统一版本,组件引进来就是版本漂移,A 模块 3.4.1 的 MyBatis-Plus、B 模块 3.5.x,跑起来才报错。 - 短信和 JWT 都是登录的前置条件:前者负责把验证码送到用户手里,后者负责把"登录成功"这个状态封成 stateless 的凭证。
工程终态:第一章结束时长什么样
实读第一章代码快照,模块清单与讲义 3.2.2 的模块表一致,八个模块全部就位(mf-dubbo-mongo在这一章还是空壳,只建了目录)。
mf/ # 父工程(聚合 + 统一版本管理) ├── pom.xml ├── mf-autoconfig/ # 自动装配组件(第三方能力封装) │ └── src/main/ │ ├── java/com/mf/autoconfig/ │ │ ├── mfAutoConfiguration.java │ │ ├── properties/SmsProperties.java │ │ └── template/SmsTemplate.java │ └── resources/META-INF/spring.factories ├── mf-model/ # 实体类模块 │ └── src/main/java/com/mf/model/domain/ │ ├── BasePojo.java # created / updated 自动填充基类 │ └── User.java # 继承 BasePojo ├── mf-commons/ # 工具类模块 │ └── src/main/java/com/mf/commons/utils/JwtUtils.java ├── mf-dubbo/ # Dubbo 模块的父级(仅作管理) │ ├── mf-dubbo-interface/ # Dubbo 接口(契约) │ │ └── com/mf/dubbo/api/UserApi.java │ ├── mf-dubbo-db/ # Dubbo 服务(数据库部分) │ │ └── com/mf/dubbo/ │ │ ├── DubboDBApplication.java │ │ ├── api/UserApiImpl.java │ │ ├── handler/MyMetaObjectHandler.java │ │ └── mappers/UserMapper.java │ └── mf-dubbo-mongo/ # Dubbo 服务(MongoDB 部分,暂空) └── mf-app-server/ # 与手机端交互的入口 └── com/mf/server/ ├── AppServerApplication.java ├── controller/LoginController.java └── service/UserService.java两个关键产物在这一章定型:mf-autoconfig里的短信组件被mf-app-server直接@Autowired就能用,证明自动装配真的生效;mf-model的User继承BasePojo,把created/updated从业务代码里彻底摘掉。
可复用套路清单
这一章的价值不在"写了哪些类",而在于几个会反复出现的模式。后面每个业务域基本都是在套这几张模板。
| 套路 | 本章用在哪 | 后续哪里还会再用 |
|---|---|---|
第三方能力封装四件套:Properties+Template+AutoConfiguration+spring.factories | 阿里云短信 | OSS 上传、人脸识别、内容审核,凡是"接一个云厂商 SDK"都是同一套 |
Dubbo 服务三件套:interface契约 +Impl实现 + 消费者@DubboReference | UserApi/UserApiImpl | 后续每个新业务域(动态、推荐、IM)都要新建一组 |
| 验证码 + Redis 组合 | 登录验证码,5 分钟 TTL | 凡是短时效数据(临时凭证、一次性令牌、防重复提交标记)都能照此办理 |
| JWT 签发与校验 | 登录后签发 token,携带id+mobile | 后续所有需要身份识别的接口都靠它,配合统一鉴权拦截器 |
BasePojo+ 自动填充 | created/updated | 后续所有持久化实体都继承它,不再手写时间字段 |
四件套的骨架长这样,换个第三方 SDK 只需替换SmsTemplate里的实现。
# mf-autoconfig/src/main/resources/META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.mf.autoconfig.mfAutoConfigurationpackagecom.mf.autoconfig;importcom.mf.autoconfig.properties.SmsProperties;importcom.mf.autoconfig.template.SmsTemplate;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.springframework.context.annotation.Bean;// 自动装配入口:被 spring.factories 声明后,其他工程引入本模块即自动生效@EnableConfigurationProperties({SmsProperties.class})publicclassmfAutoConfiguration{// 把配置类里的参数注入 Template,再以 Bean 的形式暴露给使用者@BeanpublicSmsTemplatesmsTemplate(SmsPropertiesproperties){returnnewSmsTemplate(properties);}}Dubbo 三件套的分工值得记牢:mf-dubbo-interface只放UserApi接口,被提供者和消费者共同依赖;mf-dubbo-db里UserApiImpl用@DubboService暴露;mf-app-server里用@DubboReference(新版本注解,等价于旧的@Reference)引用。接口模块不依赖实现模块,这是解耦的关键。
消费者端的配置也是固定模板,注意registry.address用spring-cloud://localhost(走 Nacos 注册中心),不是nacos://。
# mf-app-server/src/main/resources/application.ymlserver:port:18080# Web 端口spring:application:name:mf-app-serverredis:# 验证码存这里host:192.168.136.160port:6379cloud:nacos:discovery:server-addr:192.168.136.160:8848dubbo:registry:address:spring-cloud://localhost# 由 Nacos 托管注册发现consumer:check:false# 启动时不强校验提供者是否存在本章的关键坑回顾
坑都集中在"配置写对但装不上/发现不到",而且很多是静默失败——不报错,就是没效果。
| 坑 | 症状 | 解法 |
|---|---|---|
spring.factories路径写错 | 短信组件@Autowired注入失败或注入为 null,启动无异常 | 必须放在resources/META-INF/spring.factories,文件全名不能差一个字母;先确认模块已install |
dubbo.scan.base-packages写错 | 提供者启动正常,但注册中心里看不到服务 | 路径要精确到com.mf.dubbo.api,与@DubboService所在包一致 |
spring-cloud://与nacos://混淆 | 服务注册不上,或消费者发现不到提供者 | 走 Nacos 注册中心时统一用spring-cloud://localhost |
| Web 端口与 Dubbo 协议端口混用 | 两个服务抢占端口,或调用超时 | Web 用server.port(18080/18081),Dubbo 协议用dubbo.protocol.port(20881),两条线分开 |
实体没实现Serializable | Dubbo 调用时报序列化异常 | 传输对象(如User)必须实现Serializable,BasePojo已实现,继承即可 |
| JWT 密钥硬编码、payload 放敏感信息 | 密钥泄露即全站伪造;claims 可被 base64 解码 | 密钥外置到配置,payload 只放id、mobile这类非敏感标识 |
验证码开发态写死123456忘记改回 | 上线后任意手机号都能用123456登录 | 联调期临时写死可以,提交前必须换回随机 6 位并走短信通道 |
tb_user.mobile缺唯一索引 | 并发首次登录时同一手机号插入两条 | 给mobile加唯一索引,并捕获DuplicateKeyException兜底 |
多模块工程未先install公共模块 | 依赖mf-model报找不到坐标 | 先mvn install父工程与公共模块,再做子模块开发 |
| Maven 依赖未统一版本管理 | 运行期NoSuchMethodError、版本漂移 | 版本统一写在父工程<dependencyManagement>,子模块只写坐标不写版本 |
自检清单
跑通本章的标准不是"代码能编译",而是下面八项逐条成立。每一项都给了验证方式。
| 检查项 | 怎么验 |
|---|---|
| 环境已就绪 | Linux 里docker ps能看到 MySQL、Redis、Nacos;Maven 能拉到依赖 |
| 服务已注册到 Nacos | 打开 Nacos 控制台http://<ip>:8848,服务列表里有mf-app-server与mf-dubbo-db |
| 短信能真正发出 | 调用smsTemplate.sendSms("真实号码","1234"),手机收到短信(不是只打印日志) |
| 验证码能落进 Redis | 调POST /user/login后redis-cli执行keys CHECK_CODE_*,能看到键且 TTL 约 300s |
| 登录能拿到 token | 调POST /user/loginVerification,返回体里有token字段且是一串A.B.C |
新用户自动注册且isNew=true | 用未注册手机号登录,DBtb_user新增一行,返回值isNew=true |
老用户isNew=false | 用已注册手机号再登录一次,tb_user不新增,返回值isNew=false |
created字段自动填充 | 新注册用户那一行created不为空,且值约等于插入时刻 |
练习建议
不看讲义、把登录链路独立再实现一遍,是最有效的检验——建议改用不同的包名/类名,验证自己记住的是结构而不是代码位置。
补一道并发防线。tb_user.mobile加唯一索引,并处理DuplicateKeyException。
-- 改哪里:数据库执行ALTERTABLEtb_userADDUNIQUEINDEXuk_mobile(mobile);// 怎么验证:并发调用登录,同一手机号插两次只成功一次,另一次被捕获而非抛 500try{LonguserId=userApi.save(user);user.setId(userId);isNew=true;}catch(DuplicateKeyExceptione){// 并发下已被别人抢先注册,回查即可user=userApi.findByMobile(phone);isNew=false;}补一个频控。给发验证码接口加"同一手机号 60 秒一次 / 一天 10 条",用 Redis 计数 + TTL。
// 改哪里:UserService#sendMsg 开头StringlimitKey="SMS_LIMIT_"+phone;Longcount=redisTemplate.opsForValue().increment(limitKey);if(count!=null&&count==1){redisTemplate.expire(limitKey,Duration.ofDays(1));// 首次设置日上限 TTL}if(count!=null&&count>10){thrownewRuntimeException("今日验证码次数已达上限");}// 怎么验证:连续点 11 次发短信,第 11 次返回受限;把键删掉后恢复把硬编码清掉。JWT 密钥mfxs123、默认密码123456、Redis key 前缀CHECK_CODE_都提到配置或常量类。
# 改哪里:application.yml 增加mf:jwt:secret:${MF_JWT_SECRET:change-me}expire-seconds:3600// 怎么验证:全局搜索代码里不再出现 "mfxs123" 与 "123456" 字面量// 只保留常量类或配置读取给验证码加一次性语义。校验成功后立即删除 Redis 里的验证码(现有代码已经做了delete),并确认删的是同一个 key,避免大小写或前缀不一致导致删了个寂寞。
常见误解澄清
- 以为登录做完项目就过半了。其实只是把骨架打通了,业务功能(动态、推荐、IM)一个都还没开始。
- 以为学到的是一次性代码。短信组件、Dubbo 三件套、JWT 工具类都是可迁移的套路,后面每个模块都在复用它。
- 以为
123456可以留在代码里。联调临时写死可以,交付前必须换回随机验证码并接通短信。 - 以为流程跑通就算完成。还要补防线:唯一索引挡并发、频控挡刷短信、密钥外置挡泄露。
下一篇预告
后续将进入"完善用户信息":上传头像、人脸识别、个人资料管理,并引入统一鉴权与统一异常处理。它会直接吃掉本章的产物——短信组件那套自动装配套路会被复用来接 OSS 与人脸识别 SDK,JWT 会在统一鉴权拦截器里被解析出id,BasePojo会被新的用户资料实体继续继承。本章把骨架和凭证体系立住了,下一章才有地方往上长业务。
官方文档
- 阿里云短信服务
- JSON Web Token 官方测试站
- Spring Boot 自动装配与 spring.factories
- Apache Dubbo 注解驱动
总结
- 本章的产出是一条可运行的登录链路:注册中心能发现服务,短信能发出,验证码能落 Redis,登录能拿 token,新用户能自动注册。
- 真正沉淀下来的不是八个模块的目录,而是五套可复用套路:第三方封装四件套、Dubbo 三件套、验证码 + Redis、JWT、
BasePojo。 - 容易翻车的地方几乎都在配置静默失效(
spring.factories路径、Dubbo 扫描包、注册协议),排查时先看"服务有没有注册上去",再往下查。 - 流程跑通只是及格线,唯一索引、发短信频控、密钥外置这三条防线补上,才算把这一章的隐患真正修掉。