智汇笔记后端实战(一):从架构选型到踩坑排雷,我是如何落地 JWT+Redis 企业级双校验登录模块的?
大家好,我是《智汇笔记(SmartNotes)》项目的后端开发李昌。目前我们的实训项目已经正式进入代码攻坚阶段。本周,我的核心技术进度是:完成了系统底层基建,并成功从 0 到 1 落地了高安全级别的用户认证模块。
在实现登录功能时,我没有采用学校教的传统单体 Session 方案,也没有使用简单的裸奔 JWT,而是对标大厂规范,采用了 JWT + Redis 双校验架构。本文将记录我的技术选型思考,以及在实际代码落地时踩过的坑。
一、 技术理解:为什么我放弃了熟悉的 Session?
在着手写代码前,我梳理了目前主流的认证方案。传统 Session 在单体项目中确实简单,但在我们目前前后端分离场景下,它暴露出三个致命缺陷:
状态集中:服务器需要耗费内存保存 Session,扩容复杂。
跨域困扰:前后端分离跨域场景下,Cookie 和 Session 的会话控制不够直观。
高并发瓶颈:每次请求都要查内存,缺乏灵活性。
于是我把目光转向了 JWT。但纯粹的 JWT 也有一个无法忍受的痛点:一旦签发,在过期前天然难以主动失效。如果用户点击“退出登录”或者账号被盗,服务端无法强制让发出去的 JWT 作废。
最终架构敲定:JWT + Redis 方案
JWT 负责无状态解析:承载用户 ID,前端每次请求放在 Header 中,后端拦截器瞬间解析,无需查库。
Redis 负责状态控制:登录成功时,同步把 Token 写入 Redis 并设置相同 TTL。退出登录时,删除 Redis 中的 Token 即可实现“立即失效”。
二、 工程落地:
严格的 DTO/VO 分层隔离
入参绝对不用 User 实体类,而是新建 UserLoginDTO。避免前端恶意构造请求,越权提交 id 或 create_time 等敏感字段。
出参封装为 UserLoginVO,只返回必要的 id、username 和 token,彻底隔绝密码泄露可能。
参数校验前置到 Controller
结合 Spring Validation,在 Controller 使用 @Validated,在 DTO 字段加上 @NotBlank。账号密码为空的请求直接在网关层被拦截,绝不让它们进入 Service 层消耗数据库资源。密码单向不可逆加密
严禁明文存库!在 Service 层,将前端传来的明文密码利用 Spring 的 DigestUtils 进行 MD5 摘要计算后,再与数据库比对。服务端永不做明文逆向解析。拦截器的“三重门”验证
我编写了 LoginInterceptor 拦截器,每次请求必须通过三关:① Header 中必须有 Bearer Token;② 该 Token 必须在 Redis 中存在(防注销);③ JWT 签名解析必须合法且未过期。异常统一收口,拒绝抛 null
当账号不存在或密码错误时,Service 层绝不 return null,而是直接 throw new PasswordErrorException()。交由全局异常处理器统一包装为标准 Result JSON 响应给前端。
三、 纸上得来终觉浅:实战踩坑与排雷复盘
理想很丰满,现实很骨感。在和前端联调的过程中,由于开发环境和工具配置,我踩了无数个坑。以下是我复盘出的几个经典案例,附上排查路径,希望能帮到大家:
坑 1:HTTP 状态码的“欺骗”
现象:前端反馈接口看起来成功了(HTTP Status 200),但页面没发生跳转。
原因与排查:我使用了全局统一的 Result<T> 封装。即使触发了异常,全局处理器捕获后也会返回 HTTP 200,只是 JSON 体里的业务 code=500。
修复规范:制定联调契约:前后端统一以 body.code === 200 判断业务成功,绝不能只看网络层的 200 状态码。
坑 2:注册入库成功,但接口依然报 500
现象:数据库里明明多了一个新用户,但前台一直提示“服务器内部错误”。
原因与排查:查看控制台堆栈发现是 RedisConnectionFailureException。由于我采用了 Redis 联动,Insert 语句执行成功后,写入 Redis Token 时本地 Redis 服务意外宕机,导致报错回滚。
教训:认证模块强依赖 Redis 环境。这提醒我后续在复杂的分布式写入时,业务一致性需要更完善的事务补偿或明确的错误降级策略。
坑 3:Lombok 导致“找不到 Getter”与 IDE 一片红
现象:编译时狂报 getUsername() 找不到,实体类字段飘红。
原因与排查:本地 IDE 的 Annotation Processing 未正确开启,导致 @Data 没能在编译期生成方法。
修复:为了降低后期上线构建时对环境的依赖,我将部分核心 DTO 改为使用显式构造器并手写 Getter/Setter,这虽然稍显冗余,但却是最稳妥的做法。
坑 4:拦截器的“误杀”
现象:刚写完注册接口,前端一发请求就喜提 401 Unauthorized。
原因与排查:拦截器写得太死,把所有 /api/** 都拦了。新用户还没注册哪来的 Token?
修复:在 WebConfig 的 InterceptorRegistry 中,明确加入 .excludePathPatterns("/api/user/login", "/api/user/register") 将鉴权接口放行。
四、 阶段总结与下周计划
通过这次登录模块的从 0 到 1 落地,我深刻体会到:企业级的认证绝不是“写个 SELECT 查询接口”这么简单,而是一套涵盖网络协议、密码学、内存缓存与工程规范的完整生态系统。
JWT 解决了分布式系统的扩展性,Redis 补足了状态的可控性,而规范化分层与全局异常处理则保证了代码的极高可维护性。
下周进度预告:
底座已经搭好,下周我将全力推进《智汇笔记》的核心 AI 业务模块。重点攻克:大模型长耗时接口的高并发控制、基于 Spring Boot 的SseEmitter(服务器发送事件)流式打字机响应。