site logo

Marico's space

用 Spring Boot 和 Spring Security 打造生产级三层审批引擎

服务器技术 2026-04-25 22:54:16 3

一个深入剖析多级审批流程、无状态 JWT 认证、服务层授权守卫机制以及完整 CI/CD 流水线的实战项目复盘。

为什么要做这个

大多数后端教程教的都是 CRUD:注册、登录、查列表。这些当然有用,但说实话,它们离真实生产环境差得远。

现实情况是,几乎每家公司都有某种形式的审批流程。报销单需要主管签字,新供应商合同需要法务审核,基础设施变更需要运维批准。而且这些流程几乎都是多级的——一个人拍不了板。

我要做的就是一个能让请求在多个审批层级间逐级流转的系统,每层都有基于角色的访问控制。

整体架构

系统分为三个层级:

  • 第一级 —— 发起人(Initiator):创建请求的人。可以查看自己提交的请求及当前状态。
  • 第二级 —— 审批人(Approver):通常是直属经理或团队负责人。可以审批或拒绝自己队列中的请求。
  • 第三级 —— 最终审批人(Final Approver):通常是高管或部门负责人。处理被升级的请求,或那些金额/影响较大、第二级无权单独审批的请求。

无状态 JWT 认证

所有 API 请求都通过 JWT 进行身份验证。Token 中携带用户的角色和层级信息,在 API 网关层完成验证,在请求到达服务层之前就完成过滤。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/auth/**").permitAll()
            .requestMatchers("/api/requests/**").hasAnyRole("INITIATOR", "APPROVER", "FINAL_APPROVER")
            .anyRequest().authenticated()
        )
        .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    return http;
}

服务层授权守卫

保持代码整洁的关键在于善用 Spring Security 的方法级安全注解。过去我们可能在每个方法里手动检查角色,现在只需要在方法声明处标注所需角色即可:

@PreAuthorize("hasRole('APPROVER') and #request.tier == 2")
public ApprovalResponse approveTier2Request(RequestDTO request) {
    // 业务逻辑
}

每个层级都有对应的 Service 类来处理该层级的请求。Controller 层尽量保持轻薄——它们只负责根据请求的层级将任务分发给相应的 Service。

CI/CD 流水线

应用跑在 Kubernetes 集群里的 Docker 容器中,CI/CD 流水线由 GitHub Actions 驱动:

name: Deploy to Kubernetes
on:
  push:
    branches: [main]
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build and push Docker image
        run: |
          docker build -t registry.mycompany.com/approval-engine:$GITHUB_SHA .
          docker push registry.mycompany.com/approval-engine:$GITHUB_SHA
      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/approval-engine \
            approval-engine=registry.mycompany.com/approval-engine:$GITHUB_SHA

实战感悟

整个项目做下来,最难的不是写代码,而是把状态机设计正确。请求在每个层级都可能面临审批通过、被拒绝、或升级这几种走向。如果这些状态流转没设计清楚,就会出现请求卡在中间悬而未决的尴尬局面。

用 Spring Security 的方法级注解替代手写角色判断,代码可读性提升了一个档次。强烈建议在项目启动前把注解体系摸透,这会省去大量后期的重构工作。

无状态 JWT 对水平扩容很友好,但随之而来的问题就是 Token 撤销不太好处理。我们的方案是 Token 有效期设短一些(15分钟),配合 HttpOnly Cookie 里存 refresh token 的方式来解决这个矛盾。

原文链接:https://dev.to/deepsan_bhandari/how-i-built-a-3-tier-approval-engine-with-spring-boot-and-spring-security-4ke9