实习初期:环境与流程
进入公司后,项目处于启动期,我参与了基础模块的开发。入职前几日主要是环境搭建和熟悉规范。
- 基础配置:开通飞书、邮箱等办公权限。
- 代码拉取与环境:使用 Git/SVN(阿里云云效)克隆项目,配置前后端开发环境。重点解决依赖问题,确保项目能跑起来。
- 工具熟悉:Mentor 指导了 Debug 技巧和快捷键,避免操作层面的时间浪费。
接下来的一两周是深度熟悉阶段。任务包括梳理目录结构、理解数据库设计、熟读 Common 通用包,以及掌握 Git 和 MyBatis-Plus 的使用。通过编写简单 Demo 验证对框架的掌握程度,并尝试寻找少量 Bug 提交审核。
核心模块实战
权限管理模块
权限校验是企业系统的基石。我们采用了自定义注解配合 AOP 切面的方式。
首先定义一个 Auth 注解,标记需要权限校验的方法:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auth {
String[] value() default "";
}
接着实现 AuthAspect 切面,拦截标注了 @Auth 的方法。这里利用 Spring AOP 的 @Pointcut 表达式 @annotation(auth) 精准定位目标方法,并将注解实例注入参数中供后续逻辑使用。
在 aroundAuth 通知中,我们获取当前登录用户信息,提取角色并校验是否包含在注解定义的权限列表中。如果校验失败,直接抛出业务异常;如果成功,则放行请求。值得注意的是,这里还处理了参数注入的逻辑,确保用户上下文能自动传递给业务方法。
除了注解切面,我们还实现了 LoginFilter 过滤器作为请求入口的第一道关卡。初始化阶段会将白名单 URL 解析为 PathPattern 缓存,避免每次请求都重复解析。核心过滤逻辑 doFilter 会判断请求是否命中白名单,未命中的请求进入认证处理流程。
认证处理分为普通用户和 OpenAPI 两种场景。对于普通用户,从 Header 解析 Token 并校验;对于 OpenAPI,则校验 API Key 和版本头。无论成功与否,最终都会在 finally 块中清理用户上下文,防止内存泄漏。
Token 管理与会话控制
Token 服务采用 JWT + Redis 的组合方案。JWT 负责无状态签名,Redis 负责存储用户状态以实现主动失效。
创建 Token 时,生成 UUID 作为唯一标识,构建 Claims 载荷,使用 HS512 算法签名,并将用户信息存入 Redis,Key 格式为 LOGIN_USER:{tokenId}:{username},设置过期时间与 Token 一致。
刷新 Token 是一个容易出问题的环节。当检测到剩余有效期不足 30 分钟时,系统会生成新 Token 并更新 Redis 中的过期时间。关键点在于必须同时使旧 Token 失效。我们在续期逻辑中增加了 invalidateOldToken 调用,解析旧 Token 获取其 ID 和用户名,构造对应的 Redis Key 并删除,彻底禁用旧令牌,避免同一账号多 Token 有效带来的安全风险。
业务实战:企业模块
在企业 CRUD 操作中,缓存策略至关重要。
以全量数据查询为例,我们采用了双重校验加锁的模式来防止缓存击穿:
public List<Company> cacheAll() {
final String cacheKey = "company:all";
List<Company> cachedList = redisService.get(cacheKey, List.class);
if (cachedList != null) {
return cachedList;
}
synchronized (cacheLock) {
cachedList = redisService.get(cacheKey, List.class);
if (cachedList != null) {
return cachedList;
}
List<Company> list = list();
redisService.set(cacheKey, list, 3600);
return list;
}
}
先查缓存,若为空则加本地锁,锁内再次检查缓存,确认不存在后再查库并回写。这种写法在保证性能的同时兼顾了高并发下的数据一致性。
分页查询部分,通过 PageUtil.getPage 工具类统一封装了分页元数据的透传和 VO 转换逻辑,避免了 Controller 层重复编写样板代码。参数校验则利用了 Spring 的 @Validated 注解,结合 JSR 380 规则,自动拦截非法入参。
开放平台集成
针对第三方系统访问,我们设计了独立的 Token 生成接口。流程上先校验 API Key 和 Secret 的有效性,然后撤销该 Key 下所有旧 Token(保证单 Key 单 Token),再生成新的随机 Token 并存入 Redis。这样既保证了安全性,又简化了第三方的鉴权逻辑。
工程规范与工具
为了提升开发效率和代码质量,我们封装了一系列通用能力。
判空工具类:推荐使用 Hutool 的 ObjectUtil 或 StringUtils。区分 isEmpty(null 或空串)、isBlank(null 或空白字符)和 isNotEmpty 等场景,避免手写繁琐的条件判断。
日志框架:基于 Logback 配置了异步输出。通过 ASYNC_FILE 和 ASYNC_STDOUT 将日志写入队列,主线程无需等待 IO 完成即可继续执行,显著提升了系统吞吐量。同时配置了按小时滚动和保留 30 天的策略,防止磁盘爆满。
分布式锁:基于 Redisson 实现了 @Lock4j 注解。通过 AOP 解析 SpEL 表达式动态生成锁 Key,支持设置等待超时和看门狗机制。在事务切面之前执行(@Order(-10)),确保锁逻辑优先于事务提交,避免死锁或数据不一致。
异步编程:封装了 CompletableUtil,基于自定义线程池提供异步执行能力。相比传统线程,它支持链式回调、异常处理和结果组合,非常适合日志记录等非阻塞场景。
踩坑记录与修复
实习期间也遇到了一些典型问题,记录下来以便避坑。
缓存失效问题:曾发现 removeCache() 方法被调用但缓存并未真正失效。排查后发现是因为核心业务方法直接操作数据库,从未读取过缓存,导致缓存逻辑形同虚设。修复方案是将缓存读取逻辑接入到所有对外查询接口中,确保缓存真正参与业务流程。
续期后旧 Token 未失效:在 Token 刷新逻辑中,仅生成了新 Token 并更新了 Redis 中的过期时间,但未删除旧 Token 对应的 Key。这导致攻击者可以复用旧 Token 进行非法操作。修复方案是在刷新成功后,显式调用删除旧 Key 的方法,确保旧令牌立即失效。
这些实战经验不仅加深了对技术原理的理解,也让我明白了企业级开发中对稳定性、安全性和可维护性的严格要求。


