ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot律所管理系统实战:权限设计、JWT认证与状态机详解

Spring Boot律所管理系统实战:权限设计、JWT认证与状态机详解 做了几个月的律师事务所管理系统从需求梳理到数据库设计再到前后端联调踩了不少坑也积累了一些实打实的经验。今天把整个项目的核心设计思路、技术选型和代码实现细节一次性整理出来既是对自己工作的复盘也希望能给正在做类似管理系统的同学一些参考。这个项目是基于 Spring Boot 的律师事务所管理系统覆盖了律所日常运营中最关键的几个环节案件管理、客户管理、文档管理、收费管理和日程提醒。对于学 Java 的同学来说它是一个非常典型的全栈实践项目几乎涵盖了 Spring Boot 后端开发的所有核心知识点从权限认证到事务处理从文件上传到定时任务全都能接触到而且业务场景足够具体不会像电商系统那样烂大街写进简历里也更有辨识度。1. 项目整体定位与业务拆解1.1 律所管理系统的核心业务场景在开始敲代码之前我花了大量时间做业务调研。很多人拿到这类题目就急着建表心里想的就是案件表、客户表、律师表三张表一建CRUD一写完事。实际上这是项目做不好最根本的原因。律所的管理痛点和普通企业管理系统有明显的差异。最核心的倒不是技术而是业务规则的多变和责权关系的高度敏感。举个例子一个案件从当事人咨询开始到委托代理再到立案、开庭、判决、执行这中间涉及大量关键时间节点任何一个节点错过都可能导致严重后果。如果败诉或者错过上诉期产生的责任纠纷不是赔钱能解决的是会影响律师执业资格的事。再比如律师费的管理一个案件可能分阶段收费有前期费用、风险代理费用、胜诉后的提成每一笔钱怎么分、什么时候到账、跟案件的哪个里程碑挂钩这些都是必须建模清楚的业务规则。系统面向的核心用户其实就是三大类律所的管理者一般是合伙人或主任、执业律师团队、行政财务人员。但实际使用中还有一类用户经常被忽略——前台或行政助理他们往往是录入案件信息、登记客户来访记录的实际操作者。所以系统不能只做成给律师用的工具而是要兼顾管理视角和执行视角。我在设计的时候就先把业务场景拆成了几个关键模块案件全生命周期管理从咨询登记、立案审批、办理过程到结案归档客户关系管理包含客户基本信息、关联案件、服务记录和回访提醒文书与证据管理法律文书模板、案件文件归档、电子卷宗管理收费与提成管理收费计划、到账记录、律师提成结算日程与任务管理开庭提醒、时效届满提醒、任务分派。这些模块之间不是孤立的它们通过案件这条主线串联起来。比如一个客户到了前台录入咨询信息系统自动生成一条待评估记录合伙人评估通过后转为正式委托案件系统根据案件类型触发时效提醒规则律师在办理中上传文书材料结案后财务根据收费计划做结算。这套逻辑理顺之后数据库设计就有了清晰的方向代码结构也不会东一榔头西一棒子。1.2 用户角色与权限模型设计权限设计是我在这个项目里花时间比较久的部分。律所系统的权限敏感性极高律师之间的案件信息是相互保密的合伙人能看到全所的业务数据普通律师只能看到自己经手的案件前台能录入信息但不能查看案件的实质内容财务能看到收费数据但是不能看证据材料。项目采用了基于 RBAC基于角色的访问控制模型的权限框架配合 Spring Security 实现。我定义了这么几个角色系统管理员、合伙人、执业律师、助理、前台、财务。每个角色对应不同的权限集合具体到接口层面做粒度控制。最核心的权限控制点在案件数据的归属上普通律师和助理登录后只能操作自己名下或者与自己关联的案件合伙人和管理员可以看到全所案件财务只能访问与收费相关的数据前台只能访问客户模块和咨询登记模块。这是整个项目中最具律所特色的规则也是最能体现系统价值的地方。数据权限和功能权限是两个维度功能权限你可以用注解轻松实现数据权限就得动不少脑筋了。我在设计时采用了比较直接的做法在案件表中维护主办律师ID和协办律师IDs字段在 MyBatis 的 SQL 层通过拦截器动态拼接过滤条件核心 SQL 都强制带上当前用户可见范围的约束条件而不是在业务层手动判断。这套方案虽然不如引入专门的数据权限框架那么高大上但胜在逻辑直观、可控性强出了问题也容易排查适合这个体量的项目。真要做复杂的企业级权限中心那又是另外一套玩法了。1.3 为什么是 Spring Boot 而不是其他方案选型这件事其实没有绝对的对错只有适合不适合。这个项目我选了 Spring Boot 3.x 作为后端基础框架理由很实在第一Spring Boot 的自动配置机制极大降低了项目搭建成本。用传统 SSM 整合的时候光 Spring 和 MyBatis 的 XML 配置就能写上几百行Spring Boot 通过 starter 机制把这些繁琐的配置全部封装掉了我可以在几分钟内拉起一个可运行的项目骨架。项目里有句话说得很对Spring Boot 不是新框架而是对既有 Spring 生态的一次开箱即用封装升级让开发者能更专注于业务本身。第二生态成熟坑少。Spring Boot 经过这么多年的发展各种问题的解决方案在网上几乎都能找到遇到报错不至于卡死。而且它和 Spring Security、MyBatis Plus、Redis 这些项目里的核心依赖集成都非常平滑社区资料丰富对初学者友好。第三部署方式灵活。打包成 fat jar 后一条 java -jar 命令就能跑起来不需要额外装 Tomcat 容器对于演示和交付都非常方便。当然也有一些备选方案被我否掉了。用 Python 的 Django 或 Flask 也能做但考虑到项目需要大量的 Java 类库支持比如 POI 操作 Excel、iText 处理 PDFJava 生态更成熟用若依之类的快速开发平台做二次开发虽然快但对于想学习核心原理的同学来说很多东西会被框架屏蔽掉反而不利于真正理解系统运作的机制。2. 系统架构与核心技术实现2.1 后端架构分层与请求处理流程项目的后端采用经典的分层架构Controller 层、Service 层、Mapper 层中间由实体类和数据传输对象衔接。分层的目的不是制造麻烦而是让每一层各司其职互不越界。Controller 层负责接收和校验请求参数不写业务逻辑Service 层负责业务规则的实现和事务控制Mapper 层负责与数据库交互只做数据存取DTO 层用于接口入参与出参的定义避免直接用数据库实体暴露给前端。画一个请求流转链路你就明白了前端发起 HTTP 请求经过 Spring Security 的认证过滤器链识别用户身份再经过拦截器校验接口权限到达 Controller 后做参数校验然后调用 Service 层完成业务处理Service 内部调用 Mapper 操作数据库最终结果统一封装为 Result 对象返回给前端。这两个点在项目里一定要注意统一返回对象和全局异常处理。如果每个接口返回结构都不一样前端联调时会产生大量的无效沟通如果异常处理分散在各层错误信息要么暴露了内部细节要么被吞得干干净净。我统一封装了 Result 类包含 code、message、data 三个字段配合 RestControllerAdvice 写全局异常处理器业务异常、参数校验异常、未知异常分别返回不同的 code这样前后端之间的协作会顺畅很多。2.2 核心依赖与配置说明项目的 pom.xml 文件里整合了这些核心依赖每个都是我实际用过后觉得必要的依赖版本建议用途说明spring-boot-starter-web跟随 Spring Boot 版本Web 基础能力内嵌 Tomcatspring-boot-starter-security跟随 Spring Boot 版本登录认证与接口鉴权mybatis-plus-boot-starter3.5.x数据访问层内置通用 CRUD 与分页插件mysql-connector-j8.xMySQL 驱动lombok跟随 Boot 版本消除实体类的样板代码jjwt0.11.x生成和解析 JWT Tokenspring-boot-starter-data-redis跟随 Boot 版本缓存热点数据和存储 Token 黑名单spring-boot-starter-validation跟随 Boot 版本参数校验框架hutool5.8.x工具类库处理日期、文件、加密等操作knife4j4.x接口文档在线调试关于版本遇到过不少同学直接把依赖版本写成最新版结果依赖冲突搞到怀疑人生。我的经验是 Spring Boot 父工程已经统一管理了大量的依赖版本不需要手动指定如果手动指定尽量选择跟 Boot 主版本兼容的稳定版本。比如 Spring Boot 3.x 对应的 security、redis、validation 这些 starter 的版本都由父工程管理直接引坐标就行不要画蛇添足。2.3 JWT 认证与登录流程实现登录认证方案选了 JWT 而不是传统的 Session 方案主要原因有两个一是后端将来如果要拆分成多个服务JWT 的无状态特性方便扩展二是手机端和 Web 端共用一套认证逻辑很自然。整个登录流程是这样的用户提交账号密码后端校验通过后生成一个签名后的 JWT返回给前端前端拿到 Token 后存储在本地一般放 localStorage 或者 Pinia 的状态管理里每次请求在 Authorization 请求头里携带后端通过 Spring Security 的过滤器链解析 Token获取用户身份和权限集合完成认证。具体实现时有几个细节容易被忽略。第一个是 Token 过期的问题我设置了 24 小时的过期时间但用户在操作过程中可能需要保持长时间登录状态这就需要一个刷新机制。项目里实现的方式是后端使用 Redis 存了一份 Token 的剩余有效期当请求到达时如果发现 Token 快过期了就自动签发一个新的 Token 放在响应头里返回前端检测到新 Token 后自动更新本地存储。这个方案比自己写刷新接口简单对前端也更透明。第二个是被强制退出登录比如用户修改密码后旧 Token 还残留的问题。做法是在 Redis 里维护一个用户当前有效 Token 的键值每次请求都把请求中的 Token 和 Redis 里的比对不一致就拒绝访问修改密码后删除这个键旧 Token 立刻失效。注意JWT 本身是无法主动作废的因为它是无状态的。引入 Redis 校验实际上是引入了一点点状态牺牲部分无状态性来换取可控性这在管理系统的场景中是值得的。2.4 AOP 全局日志与数据审计律所业务有一个叫合规审计的硬性要求简单说就是谁在什么时间对哪个案件做了什么操作必须留痕。刚开始我是想着在所有业务方法里手动写日志记录代码写了一个模块就觉得不对这样的代码侵入性太强了到处是重复的记录代码而且容易漏记。后来我引入了 Spring AOP用自定义注解 OperationLog 标注在需要审计的接口方法上通过切面统一记录操作日志。注解里定义操作类型新增、修改、删除、审批等、模块名称、功能描述切面在目标方法执行后从当前登录用户上下文取出用户信息从请求上下文解析出 IP 和 User-Agent连同方法参数序列化后的核心信息一起写入操作日志表。这个设计比手动埋点干净太多而且扩展性强。以后想在日志里加一个导出 PDF 操作记录只需要在方法上补个注解不需要改任何业务逻辑。切面里还做了一个小优化对于上传的大文件内容不做序列化记录只记录文件的路径和大小防止日志表被撑爆。用 AOP 还有另一个隐藏好处我做最近操作动态这个功能时直接查操作日志表就行不需要业务表中专门维护最后修改人最后修改时间这些语义比较模糊的字段。3. 数据库设计与核心模块详解3.1 表结构与关系设计思路数据库是这个项目的地基。我前前后后设计了将近三十张表这里挑最核心的几张说一下不一个个贴建表语句了核心讲关系设计。先说用户侧的几张表sys_user用户表、sys_role角色表、sys_user_role用户角色关联表、sys_menu菜单权限表、sys_role_menu角色菜单关联表。这套五表模型是 RBAC 权限体系的经典范式用户通过角色间接获得权限每个角色下的权限可以动态调整非常灵活。案件侧是核心业务表case_info案件信息主表、customer_info客户表、lawyer_evaluation委托评审表、case_timeline案件节点时间线表、case_document案件文档表、fee_plan收费计划表、fee_record到账记录表。主表和关联表用外键语义关联实际开发中不一定要真建外键约束这点后面会讲但关系必须要清晰。时序提醒相关的是 schedule_task日程任务表和 case_deadline时效届满记录表。其实这两个表有部分重合后来我把开庭提醒归到 schedule_task把上诉期届满举证期限届满归到 case_deadline两者的提醒逻辑在实现上是一样的但业务含义不同分开维护更清晰。我特别想讲一下数据逻辑删除的设计。律所管理的案件数据从业务上是不能真正删除的——一个案件如果因为录错了要消除也应该是作废而不是抹掉否则审计会出大问题。项目里所有业务表都加了 deleted 字段0 未删除1 已删除查询时用 MyBatis Plus 的逻辑删除功能自动过滤元数据层面保证数据不会被物理销毁。3.2 案件全生命周期状态机设计案件管理是整个系统的中枢模块也是最难做的一块。案件的状态不是简单的进行中和已结束它是一个有明确流转路径的状态机。我设计的案件状态包括咨询登记有客户咨询尚未正式委托待评审前端人员录入合伙人评审是否承接已委托评审通过正式签订代理合同办理中案件进入实质办理阶段含立案、排期、调查取证等子状态已结案判决或调解生效办结归档已归档结案后确认归档材料齐备已作废评审不通过或客户取消委托。状态机的核心价值在于它把业务的合规性约束写进了代码。比如已归档状态的案件不允许再新增待办任务已作废案件不允许再上传文档待评审状态的案件只有合伙人和管理员能查看详情。我用状态枚举 状态流转表做了两层保障实体里保存当前状态流转表记录每一次状态变更的时间、操作人、变更前后状态和操作备注。这样既方便前端展示案件动态也满足了审计留痕要求。代码层面的状态变更统一走一个方法先校验当前状态是否允许流转到目标状态再执行更新和落流转记录整个过程包在事务里。我后来踩过一个坑导出 Excel 时并行查询了大量案件数据因为没加数据库行锁和状态乐观锁导致同一批数据的并发修改互相覆盖。这个问题我们放到后面排查部分细说。3.3 客户管理与保密机制客户模块看起来只是简单的增删改查但实际上因为涉及敏感个人信息保密要求非常高。我在客户表的设计里除了基本联系信息还增加了一个保密等级字段。普通客户是标准级涉及商业机密、知名企业或敏感纠纷的客户可以设定为高保密级。高保密级的客户数据默认对普通律师和助理不可见只有合伙人、管理员和被特别授权的人员能查看。这个特别授权我通过一张 client_access_auth 表来实现合伙人可以针对某个高保密客户给某个律师临时授权授权关联一个截止时间到期自动失效。这个功能看起来不大但在实际律所管理中非常实用。它解决了律师需要知道客户存在但不需要知道客户全部信息的矛盾场景。在实际的律所运转中这种基于单个客户的细粒度授权远比粗暴的角色权限要合理得多。前端在客户列表里对高保密客户做脱敏处理字段值显示为****点进去后如果没有授权会直接提示无权限。技术和业务体验互相印证这个设计值得借鉴。3.4 文书管理与文件上传文书管理是律所系统里又一大块。案件办理过程中会产生大量的合同、起诉状、证据材料、判决书这些文件既有文档类Word、PDF也有图片类扫描件、照片。文件的安全性和检索效率是这个模块的两大诉求。我采用了本地磁盘存储 数据库元数据记录的方式文件本体存储在服务器配置的目录下按案件ID/文件类型/日期的层级组织目录结构数据库的 case_document 表记录文件名、存储路径、文件大小、上传人、关联案件、文档类型合同、证据、文书、判决书等。上传接口做了三个关键处理一是文件类型的白名单校验不能只检查扩展名还要检查文件的 MIME 类型防止伪造扩展名上传恶意文件二是对上传文件统一重命名为时间戳随机数的形式避免文件名冲突同时把原始文件名存到数据库保证用户下载时能看到原名三是文件大小限制单个文档不超过 20MBPDF 扫描件如果太大提示用户压缩后上传。下载接口做了权限校验和响应头设置。文件流返回的时候必须设置 Content-Disposition 的编码规则否则中文文件名会在浏览器里乱码——这个问题我是在测试时被同事提醒才发现的效果非常直观但很容易漏。3.5 收费管理与统计报表收费模块直接关系到律所的钱任何误差都可能导致严重的信任问题。收费计划fee_plan表记录了每个案件的收费目标、收费节点和金额到账记录fee_record表记录每一笔实际到账。两者的差额就是待收款项这对律师事务所管理来说是最关键的管理指标之一。系统里支持两种常见的收费模式固定收费一次性付清或分期和风险代理按最终结果的一定比例收费。我把这两种模式用收费类型字段区分风险代理类型在评审通过时录入比例系数结算时按结案标的金额自动算出应收费金额生成待收记录和催款提醒。统计报表是基于收费记录进行的聚合查询。这里我用 MyBatis Plus 的 Wrapper 构造各种维度组合比如按律师维度统计每个人的创收、按案件类型分析各业务线的收入占比、按月度查看现金流趋势。这几个统计报表页面做出来之后律所合伙人是非常认可的因为他们之前都是靠财务手工拉 Excel现在系统里点一下就能看到。4. 项目运行与源码二次开发实操4.1 本地环境准备清单源码拿到手之后第一件事不是急着导入 IDE而是检查环境是否匹配。这个项目的运行环境和常见 Java 项目基本一致但有几个关键版本点需要注意不然会在启动环节卡很久。JDK 版本17Spring Boot 3.x 要求 JDK 17 起步如果用 JDK 8 运行会直接报不支持Maven3.8 以上IDEA 自带的 Maven 插件也行MySQL5.7 或 8.0 均可推荐 8.0注意数据库编码设置为 utf8mb4Redis5.0 以上用于缓存和 Token 校验Node.js16 以上如果前端部分是前后端分离的项目则必须。运行前建议先读一遍 README 里的快速启动文档源码包里的 SQL 脚本提供了完整的建库和初始数据。不要跳过初始数据因为管理员账号、角色数据、菜单数据都在里面少了这些表系统跑起来了也没法登录。4.2 项目启动配置五步走第一步导入数据库。用 Navicat 或命令行执行项目提供的 init.sql 脚本脚本包括了建库、建表和插入初始数据的所有操作。导入后检查一下有多少张表正常应该有三十张左右如果只执行了部分脚本后面启动会出现各种表不存在的报错。第二步修改配置文件。application.yml 里主要改三处数据源数据库地址、用户名、密码、Redis 连接信息、文件上传存储路径。存储路径特别注意要填一个本地绝对路径比如 /data/lawfile/ 或者 D:/lawfile/不要用相对路径否则生成的下载链接会找不到文件。第三步启动 Redis。本地没有安装 Redis 的话可以用 Docker 快速启动启动后测试一下连接这个步骤最容易被忽略因为后端启动时 Redis 连不上有时候报错不明显等登录或操作时才会暴露问题。第四步用 IDEA 导入 Maven 项目。等待依赖下载完成文件上传组件会自动加载。然后启动 Application 主类。日志里输出Started XxxApplication说明启动成功。第五步前端项目启动。如果是前后端分离结构进入前端目录执行 npm install 安装依赖npm run dev 启动开发服务器浏览器访问本地端口即可。首次登录用脚本里预置的管理员账号。注意如果遇到跨域问题项目里已经配置了 CORS允许前端开发服务器的地址访问不需要额外处理但如果改了前端端口记得同步调整后端允许的跨域来源列表。4.3 源码目录结构与二次开发切入点拿到源码后不要急着改代码先把目录结构摸清楚这个项目遵循的是标准的 Spring Boot 工程结构config 包全局配置类包括 Security 配置、跨域配置、MyBatis Plus 配置、Redis 配置controller 包各模块的接口层按业务模块划分如 CaseController、CustomerController、FeeControllerservice 包业务逻辑层包含接口和实现类mapper 包数据访问层mapper 接口加 XML 文件entity 包数据库实体映射类dto 包接口入参和出参对象common 包通用返回结果、异常类、常量定义、枚举类util 包工具类比如 JWT 工具、文件工具、日期工具。二次开发最简单的切入点是在现有模块上加字段和接口。比如客户模块想增加一个客户来源渠道字段流程是数据库表加一列、entity 加对应字段、DTO 加上传参和返回字段、前端列表和表单同步更新。以这个路径走一遍基本就能理解整个项目的运转方式。如果你想做更有挑战性的扩展可以考虑这几个方向接入消息通知短信或邮件做开庭提醒这个功能需要对接第三方短信服务商用 Elasticsearch 做案件全文检索支持按关键词搜索海量裁判文书增加移动端适配或小程序端这是律所管理比较刚需的场景。4.4 如何把项目写成一份好的毕设文档附源码项目交付建议作为课程设计或毕业设计代码写完了只算完成了一半另一半是文档。见过不少同学代码写得不错但文档写得非常流水账上来就是本项目基于 Spring Boot 实现了某某系统然后整页贴代码这样的论文指导老师看着也头疼。写文档的核心要点是把为什么这样做讲清楚。开头部分写清楚背景和意义这个系统解决了什么问题技术选型部分说明为什么用 Spring Boot 而不用 SSH为什么用 JWT 而不用 Session数据库设计部分画出 ER 图说明表关系设计的依据核心功能部分选两到三个有亮点的功能详细讲比如案件状态机设计和数据权限控制。流程图和架构图建议用 ProcessOn 或 draw.io 画插入论文里效果比截图代码好得多。截图代码不是不可以但比例不要太夸张控制在 30% 以下。我记得答辩时老师最常问的一个问题就是这个状态机设计有没有考虑异常情况比如案件回退文档和数据流流转图已经提前回答了这个问题答辩就会很从容。关于源码交付如果是给别人用的成品附上一份简洁的启动说明和运行环境清单是很有必要的。项目交付的时候把 README 写清楚包括数据库导入方式、配置文件修改位置、账号信息、常见问题解决方案。这个做法能显著减少你后续被找的次数。5. 高频问题排查与避坑实录5.1 常见问题速查表把项目过程中遇到的问题整理成了一张速查表每条都是实际踩过的问题现象根因分析解决方案启动报 Unable to start web server端口被占用或配置错误检查 application.yml 的 server.port改用 8081 等可用端口登录成功但马上 401Token 生成和校验的密钥不一致或 Redis 中未存 Token 缓存检查 JWT 配置的密钥、Redis 连接是否正常中文乱码数据库连接 URL 未指定字符集Http 响应未设置 UTF-8URL 追加 useUnicodetruecharacterEncodingutf8响应头设置 UTF-8文件下载提示找不到文件上传和下载的存储路径基准不一致统一用配置类读取存储根路径不要在某处硬编码跨域请求被拒绝浏览器跨域策略拦截了请求检查 Security 配置里的 CSRF 和跨域配置是否冲突Spring Security 默认会拦截 OPTIONS 预检请求日期格式显示错误前端和后端的时间格式没有对齐统一在全局配置中设置 Jackson 的日期格式化模式前端用 dayjs 对齐MyBatis 分页查不到数据分页插件未注册或页码从 0 开始确认分页拦截器已配置注意 pageNum 从 1 开始传数据库表已加字段但代码报错实体类没同步更新加字段时同步修改 entity、DTO、VO 三层对象防止遗漏5.2 并发修改导致的数据覆盖问题这个问题的排查过程我觉得很典型值得单独记录。现象是这样的合伙人登录系统后同时打开多个浏览器标签页分别处理不同案件结果在某个时刻发现其中一个案件的备注被改成了另一个案件的内容。表面上看像是系统抽风但实际上是并发冲突。排查的第一步是在后端加了日志发现两个请求确实都走了更新案件的方法继续深入发现前端页面上这两个案件共用了一个最近编辑记录的缓存对象后一个请求把前一个请求的上下文数据覆盖了。修改方案是更新案件接口增加乐观锁版本号字段前端在打开每个案件详情时单独加载数据不和全局缓存混用。另外在数据库层面原本在 case_info 表上建了物理外键后来遇到一个删除报错删除客户时因为有案件引用导致外键约束冲突前台操作者完全看不懂报错信息。最终我把物理外键去掉了改用逻辑关联 应用层校验。物理外键在低并发场景下问题不大但在系统上线后会变成性能和运维的痛点。逻辑外键配合定期编写的校验脚本既能保证数据完整性又不会卡住正常的业务操作。5.3 一个真实的高并发场景带来的反思在一次演示时为了展示系统性能我用脚本模拟了 100 个并发请求同时提交案件评审结果数据库连接池直接被打满大量请求超时。分析后发现两个原因一是连接池配置太小。Spring Boot 默认的 HikariCP 最大连接数是 10对于这种管理类系统日常够用但突增流量就撑不住了。我把最大连接数调到 50并根据服务器负载调整了最小空闲连接数。二是业务方法里的数据库操作过于频繁。原来的评审逻辑里更新案件状态和插入时间线记录是两条独立 SQL中间还夹了一个权限检查查询。微服务架构下我们强调服务粒度但在单体系统里能减少一次数据库往返就应该优化掉一次。我把这几步操作合并到一个事务方法里部分中间检查缓存到 Redis整个接口响应时间降了一大截。这个经历也让我重新理解了事务边界的意义。事务不是把所有操作都裹在一起就行而是要精确地划定哪些操作必须在同一个事务内哪些可以放到事务外面。放在事务内会拉长锁占用时间放在事务外又可能产生数据不一致。这个度需要结合具体的业务场景去权衡没有放之四海而皆准的答案。5.4 上传 PDF 时的 XSS 安全坑这个问题的踩法很有意思分享出来供大家参考。项目上线前做安全测试用扫描工具模拟恶意脚本攻击发现文件上传接口存在 XSS 漏洞。当时的第一反应是上传功能怎么会和 XSS 有关系后来才明白了。原来项目在全局过滤器里对所有的请求参数做了 HTML 标签清理用来防御 XSS 攻击。问题在于这个过滤器对所有的请求体都做了字符串清洗连上传的 PDF 二进制内容也被当成了普通文本处理导致 PDF 文件被破坏上传后无法正常打开。这个案例给了一个很重要的启示全局过滤器必须按路径排除特效。上传接口的路径应该跳过文本清洗逻辑只对纯文本表单字段做安全过滤。我在过滤器里增加了一个路径白名单凡是上传接口的请求体都不经过字符串清洗文件内容直接走二进制流同时针对文件名和表单字段值单独做 XSS 检查。这个坑本质上不是技术复杂而是方案边界没画清楚。全局安全过滤器这样的横切逻辑在设计中一定要明确它的生效范围和豁免范围不然很容易在某个边缘场景里搞出隐蔽事故。最后分享一个实用的小技巧写代码之余我想说一个对整个项目帮助很大的习惯用好 git 的提交粒度。我开发过程中坚持一个功能点一次提交提交信息写清楚做了什么、为什么这么做遇到改崩了的情况直接用 git log 找因果链用 git revert 回滚到任意历史点。项目后期集中修 bug 的时候这个习惯不知道救了我多少次。很多问题是改前面的功能时带出来的没有清晰的提交历史你很难判断是哪次修改引入了新问题。尤其是像这种完整的管理系统模块之间的隐性依赖非常多改动一个公共方法可能影响五六个业务有清晰的历史记录能大幅缩短排查时间。这个项目从头到尾做下来我的体会是管理系统看似简单无非是增删改查但真正拉开差距的是对业务的理解深度和对工程细节的把控能力。Spring Boot 提供了强大的技术底座但技术永远只是工具真正有价值的是你用这套工具解决了什么真实的业务问题。如果你也在做类似的系统不妨多花点时间去理解业务流程把状态机画清楚把权限边界想明白这套做下来无论对项目本身还是对自己的能力提升都很有价值。
返回列表