4. 拦截链路设计:API网关拦截、微服务拦截、数据库拦截、前端路由拦截
多租户SaaS系统的鉴权不能依赖单一拦截点。攻击者可能绕过前端直接调用API,也可能通过内部服务间通信跳过网关。因此,必须在请求入口、服务边界、数据访问层、用户界面四个维度建立拦截链路,形成纵深防御体系。每一层拦截失败时,后续层仍能兜底,确保租户隔离不被突破。
4.1 四层拦截链路总览
下表对比了四层拦截的核心职责、拦截粒度与典型技术实现:
| 拦截层 | 拦截位置 | 拦截粒度 | 核心职责 | 典型技术 |
|---|---|---|---|---|
| API网关拦截 | 系统入口 | 请求级 | 全局Token校验、租户识别、限流、IP黑白名单 | Kong / APISIX / Spring Cloud Gateway |
| 微服务拦截 | 服务边界 | 接口级 | 细粒度权限校验、资源归属校验、内部服务认证 | Spring Security / OPA / 自定义Filter |
| 数据库拦截 | 数据访问层 | 行级 | 强制租户ID过滤、行级安全策略、SQL改写 | PostgreSQL RLS / MyBatis拦截器 / Hibernate Filter |
| 前端路由拦截 | 用户界面 | 页面级 | 路由守卫、菜单权限、按钮级可见性控制 | Vue Router / React Router / 动态路由 |
设计原则:每一层拦截应独立可回滚。当某一层策略需要变更时,通过CI/CD流水线单独发布该层配置,不影响其他拦截层的正常运行。
4.2 API网关拦截:第一道防线
API网关是外部请求进入系统的唯一入口。网关层拦截负责快速失败,在请求到达业务服务之前完成基础鉴权。
- Token校验:验证JWT签名、过期时间、issuer合法性。无效Token直接返回401。
- 租户识别:从请求头(如X-Tenant-ID)或JWT中提取租户标识,注入请求上下文。
- 限流与熔断:按租户维度进行速率限制,防止单个租户的异常流量影响全局。
- 路径白名单:公开接口(如登录、注册、健康检查)跳过鉴权。
# Kong网关鉴权插件配置示例
plugins:
- name: jwt
config:
key_claim_name: "iss"
secret_is_base64: false
claims_to_verify:
- "exp"
- "nbf"
- name: rate-limiting
config:
minute: 100
policy: local
fault_tolerant: true
hide_client_headers: false
redis_host: "redis-cluster.example.com"
回滚策略:网关插件配置应版本化管理。当新鉴权规则导致大量误拦截时,通过CI/CD快速回滚至上一版插件配置,恢复时间应控制在1分钟以内。
4.3 微服务拦截:服务间鉴权与细粒度权限
即使请求通过了网关,微服务内部仍需二次校验。因为服务间调用(gRPC、消息队列)可能不经过网关,且网关无法感知业务层面的资源归属关系。
- 内部服务认证:服务间通信使用mTLS或预共享Token,防止未授权服务调用。
- 资源归属校验:验证当前租户是否有权操作指定资源。例如:租户A不能删除租户B的订单。
- 角色与权限:基于RBAC或ABAC模型,校验用户是否具备执行当前操作的权限。
// Spring Security 微服务拦截示例
@PreAuthorize("@tenantSecurity.hasAccess(#orderId)")
@PostMapping("/orders/{orderId}/cancel")
public ResponseEntity<Order> cancelOrder(@PathVariable String orderId) {
// 业务逻辑
}
常见陷阱:微服务拦截层容易忽略内部调用链的鉴权。务必为所有服务间接口添加认证机制,避免“内部信任”漏洞。
4.4 数据库拦截:最后一道数据防线
数据库拦截是防止数据泄露的兜底策略。即使上层鉴权全部失效,数据库层仍能强制隔离租户数据。
| 实现方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| PostgreSQL RLS | 行级安全策略,自动附加WHERE tenant_id = current_setting('app.tenant_id') | 强制隔离,无需修改SQL | 数据库连接需设置租户上下文 |
| MyBatis拦截器 | 拦截Executor,动态追加租户ID过滤条件 | 灵活,支持多数据源 | 需维护拦截器逻辑,可能遗漏某些查询 |
| Hibernate Filter | ORM级过滤器,自动注入租户条件 | 与JPA深度集成 | 仅适用于Hibernate场景 |
-- PostgreSQL RLS 策略示例
CREATE POLICY tenant_isolation ON orders
FOR ALL
USING (tenant_id = current_setting('app.tenant_id')::UUID);
回滚优先级:数据库拦截策略变更风险最高。建议在CI/CD流水线中设置“数据库策略变更”为手动审批步骤,并配备自动回滚脚本。
4.5 前端路由拦截:用户体验与权限展示
前端拦截不提供安全防护,但能提升用户体验,避免用户看到无权限的菜单或页面后产生困惑。
- 路由守卫:在路由跳转前检查用户Token和权限。无权限时重定向至403页面或登录页。
- 动态路由:根据用户角色动态生成可访问的路由表,未授权路由不注册。
- 按钮级权限:通过自定义指令或组件控制“新增/编辑/删除”按钮的可见性。
// Vue Router 路由守卫示例
router.beforeEach(async (to, from, next) => {
const token = store.state.token;
if (!token && to.path !== '/login') {
return next('/login');
}
const hasPermission = await store.dispatch('checkPermission', to.meta.permission);
if (!hasPermission) {
return next('/403');
}
next();
});
注意:前端拦截仅作为辅助手段。所有敏感操作必须在后端重复校验,前端不可信。
4.6 四层拦截链路协作流程
下图展示了请求从用户到数据库的完整拦截链路,以及每一层失败时的回滚路径:
4.7 拦截链路配置的版本化与回滚
每一层拦截策略都应作为独立配置模块纳入CI/CD流水线。下表展示了各层配置的版本化策略:
| 拦截层 | 配置存储 | 版本化方式 | 回滚触发条件 | 回滚耗时目标 |
|---|---|---|---|---|
| API网关 | 声明式YAML / 数据库 | Git + 配置中心 | 鉴权误拦截率 > 5% | < 30秒 |
| 微服务 | 代码注解 / 外部策略文件 | Git分支 + 灰度发布 | 权限校验异常数上升 | < 2分钟 |
| 数据库 | 迁移脚本 / 策略SQL | Flyway + 版本号 | 数据查询超时或隔离失效 | < 5分钟 |
| 前端 | JSON配置 / 路由文件 | Git + 静态资源版本 | 页面白屏或路由异常 | < 1分钟 |
最佳实践:在CI/CD流水线中为每一层拦截配置添加“预发布验证”阶段。使用自动化测试模拟跨租户访问、越权操作等场景,验证新策略不会破坏现有隔离规则后再推送到生产环境。