摘要
本文是微服务安全体系的第三篇,承接单租户场景下的五层防御架构与四层访问控制体系,聚焦多租户场景下的横向授权边界问题。
文章系统拆解跨租户资源共享的四种核心模式 —— 授权直访、数据同步、代理中转、结果输出,分别阐述每种模式的核心逻辑、技术实现、适用边界与工程落地要点。 本文不做纯理论推导,也不追求面面俱到,而是做体系化的工程沉淀,把跨租户授权这件事从协议、模式到落地的逻辑串起来,补全从单租户纵深到多租户横向的完整安全知识链路。
一、引言:从纵深防御到横向边界
在写完《微服务五层防御架构》和《访问控制体系纵深兜底》两篇之后,我一直在梳理安全体系的下一块拼图:当系统从单租户 SaaS 演进到多租户平台,甚至跨企业生态时,原来纵向的纵深防御怎么延伸到横向的租户之间?
前两篇讲的都是单租户内部的安全逻辑:网关层做粗粒度拦截、业务层做细粒度鉴权、数据层做行级兜底,是一套沿着请求链路自上而下的纵深防御体系。这套体系在单租户内部是闭环的,但放到多租户场景下,立刻会遇到一个高频且棘手的问题: 租户 A 发布了一批资源,租户 B 申请访问,审批通过之后,资源怎么交付给 B?是把数据同步一份过去,还是只开放访问权限?授权边界怎么划?权限怎么回收?数据怎么兜底?
所以这篇文章,我把跨租户授权的模式、技术、选型、避坑做一次体系化梳理,作为五层防御架构的多租户延伸篇,把横向的安全边界补上。
二、跨租户授权的本质与底层原则
在讲具体模式之前,先把底层逻辑讲透。很多跨租户安全事故,根源都是没搞清楚 “授权” 的本质。
2.1 本质:主权与使用权的分离
跨租户授权的核心不是 “数据转移”,而是数据主权与使用权的分离:
- 数据主权始终归资源提供方(租户 A)所有,不会因为授权而发生所有权转移;
- 资源使用方(租户 B)获得的,是限定范围、限定时间、限定用途的使用权;
- 授权结束后,使用权应当被收回,相关数据应当被清理,不得留存。
2.2 三大核心原则
所有跨租户授权设计,都必须守住这三个原则,这是安全底线:
- 最小权限原则只授予完成业务所必需的最小资源范围、最小操作权限、最短有效时间。能给行级就不给表级,能给只读就不给读写,能给 7 天就不给永久。
- 全程可审计原则从申请、审批、授权、访问到操作,全链路必须留痕。出了问题要能追溯到:谁申请的、谁审批的、什么时候访问的、访问了什么数据、做了什么操作。
- 权限可回收原则授权必须是可撤销的,撤销后必须立即失效;涉及数据同步的,撤销后必须清理对方侧的残留数据,不能 “授权容易回收难”。
2.3 授权的完整生命周期
跨租户授权不是一次性操作,而是一个完整的生命周期:申请 → 审批 → 授权生效 → 访问使用 → 到期 / 主动撤销 → 权限回收与数据清理
三、四种跨租户授权模式与工程实现
3.1 授权直访模式:基于 OAuth2 的跨租户身份透传
核心逻辑:数据不移动,身份带着权限走。租户 B 的用户通过跨租户授权流程,获得租户 A 颁发的受限访问令牌,持令牌直接访问租户 A 的资源接口,数据全程保留在 A 侧。
这是最主流、最标准的跨租户授权模式,本质是把 OAuth2 的授权范围从单租户内部扩展到跨租户场景。
时序示意图
核心工程实现
这个模式完全可以复用 Spring Authorization Server 的能力,只需要在原有单租户基础上扩展两个点:多租户客户端注册和资源端租户维度校验。
1. 授权服务器:多租户客户端配置
/** * 跨租户客户端注册配置 * 给每个合作租户注册独立的client信息,绑定授权范围 */ @Configuration public class MultiTenantClientConfig { @Bean public RegisteredClientRepository registeredClientRepository() { // 租户B的客户端注册,限定只能访问租户A的指定资源scope RegisteredClient tenantBClient = RegisteredClient.withId("tenant-b-client") .clientId("tenant_b_app") .clientSecret("{bcrypt}xxx") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("https://tenant-b.com/callback") // 核心:限定该租户可访问的资源范围 .scope("resource-a:order:read") .scope("resource-a:file:view") .tokenSettings(TokenSettings.builder() .accessTokenTimeToLive(Duration.ofHours(2)) // 令牌中写入租户标识 .claim("tenant_id", "tenant_b") .build()) .build(); return new InMemoryRegisteredClientRepository(tenantBClient); } }2. 资源服务器:租户维度校验过滤器
/** * 资源服务端跨租户权限校验过滤器 * 不仅校验令牌有效性,还要校验令牌归属租户与资源权限匹配 */ @Component public class TenantScopeFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 从令牌中提取租户标识 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication instanceof OAuth2AuthenticationToken oauthToken) { String tokenTenant = (String) oauthToken.getTokenAttributes().get("tenant_id"); Set<String> scopes = oauthToken.getToken().getScopes(); // 核心校验:租户+scope双维度匹配 boolean hasPermission = checkTenantResourcePermission(tokenTenant, scopes, request.getRequestURI()); if (!hasPermission) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } } filterChain.doFilter(request, response); } private boolean checkTenantResourcePermission(String tenantId, Set<String> scopes, String uri) { // 业务逻辑:校验该租户是否有该uri的对应scope权限 // 可对接权限点管理系统做细粒度控制 return true; } }适用边界与权衡
- 适用场景:实时接口访问、大文件在线查看 / 下载、高频轻量查询、B 侧无需二次加工的场景。
- 优劣权衡:优点是无数据冗余、实时性好、权限回收即时;缺点是访问强依赖租户 A 的服务可用性,高频访问会对 A 侧造成压力,权限粒度控制复杂度更高。
3.2 数据同步模式:授权范围内的数据分发
核心逻辑:审批通过后,把授权范围内的数据,复制一份到租户 B 的独立存储空间,B 侧在本地访问和使用数据。
数据同步模式不是全表复制,而是授权范围驱动的定向分发,分两种实现路径:
- 定时批量同步:按天 / 按小时同步增量数据,适合 T+1 报表、统计分析等实时性要求不高的场景;
- CDC 增量实时同步:基于 Canal/Debezium 监听数据变更,事件驱动同步到 B 侧,适合准实时业务场景。
架构示意图
核心工程实现
工程上分两种实现:轻量场景用 Spring Batch 定时同步,高实时场景用 CDC 事件驱动同步。核心是同步前必须脱敏,以及权限撤销后的清理机制。
以 CDC 实时同步为例,核心处理逻辑:
/** * 跨租户数据同步处理器 * 监听数据变更,脱敏后同步到目标租户 */ @Component public class TenantDataSyncProcessor { // 授权配置服务:校验该数据是否在给租户B的授权范围内 private final TenantAuthService tenantAuthService; // 数据脱敏器 private final DataDesensitizer desensitizer; // 目标租户同步发送器 private final TenantSyncSender syncSender; /** * 监听租户A的订单数据变更 */ @CanalEventListener(table = "order_info") public void onOrderDataChange(CanalEntry.RowData rowData) { String dataId = rowData.getAfterColumnsList().get("id").getValue(); String tenantId = "tenant_b"; // 1. 权限校验:该条数据是否在给租户B的授权范围内 if (!tenantAuthService.isDataAuthorized(tenantId, "order", dataId)) { return; } // 2. 数据脱敏:敏感字段不可逆处理 Map<String, String> desensitizedData = desensitizer.desensitize(rowData, "order"); // 3. 同步到租户B syncSender.sendToTenant(tenantId, "order", desensitizedData); } /** * 权限撤销时的清理回调 */ @EventListener public void onAuthRevoke(TenantAuthRevokeEvent event) { if ("tenant_b".equals(event.getTenantId())) { // 清理租户B侧所有同步的该类数据 syncSender.cleanTenantData(event.getTenantId(), event.getResourceType()); } } }适用边界与权衡
- 适用场景:B 侧需要频繁访问、做二次统计加工、对查询性能要求高、实时性要求不高的场景。
- 优劣权衡:优点是 B 侧访问性能好,不依赖 A 侧服务可用性,支持复杂二次计算;缺点是数据存在冗余,一致性有延迟,权限回收有残留风险。
3.3 代理中转模式:统一中台的流量收口
本质上它解决的是多对多授权的复杂度爆炸问题:当有 10 个租户互相授权,点对点直连会产生 45 条授权链路,管理成本极高。代理中转就是在中间加一层统一共享层,所有授权都通过这一层中转。
核心逻辑:所有跨租户访问不直连,统一经过共享中台 / 授权网关。租户 A 把资源授权给中台,租户 B 统一从中台获取资源,中台做统一的鉴权、路由、限流、审计。
架构示意图
核心运作逻辑
- 统一入口:所有跨租户请求不直连资源方,全部接入共享网关;
- 统一鉴权:网关对接统一授权中心,一次校验所有租户的身份与权限,不用每个资源方单独做鉴权;
- 统一路由:网关根据请求的目标资源,自动路由到对应租户的资源服务;
- 统一审计:所有跨租户访问日志全部在网关层收口,集中审计,不用分散在各个租户。
简单说:租户不用和每个合作方单独对接授权,只需要对接一次共享网关,所有跨租户访问都在这里完成鉴权、路由、审计。
核心工程实现
核心是 Spring Cloud Gateway 的全局过滤器,实现租户身份解析 → 权限校验 → 目标路由转发的完整流程。
/** * 跨租户共享网关全局过滤器 * 统一处理所有跨租户调用的鉴权、路由、审计 */ @Component public class CrossTenantRouteFilter implements GlobalFilter, Ordered { private final TenantAuthManager tenantAuthManager; private final GatewayRouteManager routeManager; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 1. 从请求头提取调用方租户标识与令牌 String sourceTenant = request.getHeaders().getFirst("X-Tenant-Id"); String token = request.getHeaders().getFirst("Authorization"); // 2. 解析目标资源与目标租户 String targetResource = request.getPath().value(); String targetTenant = resolveTargetTenant(targetResource); // 3. 统一权限校验:源租户是否有权访问目标租户的指定资源 boolean authorized = tenantAuthManager.checkCrossTenantAuth( sourceTenant, targetTenant, targetResource); if (!authorized) { exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } // 4. 动态路由到目标租户的资源服务 URI targetUri = routeManager.getTenantResourceUri(targetTenant, targetResource); ServerHttpRequest routedRequest = request.mutate() .uri(targetUri) .build(); // 5. 记录统一审计日志 recordAuditLog(sourceTenant, targetTenant, targetResource); return chain.filter(exchange.mutate().request(routedRequest).build()); } private String resolveTargetTenant(String path) { // 从路径中解析目标租户,约定格式:/cross-tenant/{tenant-id}/** return path.split("/")[2]; } @Override public int getOrder() { // 放在身份认证过滤器之后 return -100; } }适用边界与权衡
- 适用场景:租户数量多、多对多授权关系、需要统一安全治理与审计管控的中大型平台。
- 优劣权衡:优点是统一安全收口、可观测性强、便于集中治理;缺点是多一跳网络延迟,中台建设与运维成本高。
3.4 结果输出模式:数据不出域的安全共享
核心逻辑:租户 A 不向 B 返回原始明细数据,只返回经过计算、聚合、脱敏后的结果数据。原始数据全程不离开 A 的安全域,B 侧只能拿到计算结果。
这是安全等级最高的授权模式,也是高敏感数据场景下的首选方案。
流程示意图
核心工程实现
核心是封装结果输出层,原始数据永远不暴露,只返回计算后的结果;高敏感场景叠加差分隐私噪声注入,可直接复用之前差分隐私体系的工具类。
/** * 跨租户结果输出服务 * 不返回原始明细,只返回计算结果 */ @Service public class TenantResultOutputService { private final OrderStatisticsService statisticsService; // 复用差分隐私工具类 private final DifferentialPrivacyUtils dpUtils; /** * 跨租户订单统计查询接口 * 只返回聚合统计值,不返回明细 */ public Double getOrderAmountStatistics(String tenantId, String timeRange) { // 1. 内部查询原始明细数据(不对外) double rawSum = statisticsService.sumOrderAmount(timeRange); // 2. 高敏感场景:注入差分隐私噪声 double epsilon = getTenantPrivacyBudget(tenantId); double sensitivity = getMaxSingleOrderAmount(); // 全局敏感度 double dpResult = dpUtils.addLaplaceNoise(rawSum, sensitivity, epsilon); // 3. 只返回计算后的结果值 return dpResult; } private double getTenantPrivacyBudget(String tenantId) { // 不同租户分配不同隐私预算 return 1.0; } private double getMaxSingleOrderAmount() { // 单条数据最大变动值,即全局敏感度 return 100000.0; } }适用边界与权衡
- 适用场景:高敏感数据共享、统计分析场景、强合规要求的行业。
- 优劣权衡:优点是数据不出域,安全性最高;缺点是灵活性差,只能支持预先定义好的计算场景,无法支持灵活的明细查询。
四、跨租户安全技术体系落地
模式解决的是 “资源怎么交付” 的问题,而安全体系解决的是 “边界怎么守” 的问题。结合传统数据安全体系的合理内核,落地到微服务架构上,跨租户安全可以分为四层来建设,和单租户五层防御体系形成互补。
4.1 网络层:租户边界的基础隔离
网络层是第一道物理边界,目标是做到租户之间网络默认不通,授权之后才开放指定通道。
- 基础隔离:租户之间做 VPC / 子网级隔离,跨租户流量不允许内网直连,必须经过公网出口或专门的共享中转节点;
- 传输安全:跨租户通信强制开启 mTLS 双向认证,防止链路窃听与篡改;
- 边界防护:WAF 层增加跨租户访问专项规则,拦截越权探测、扫描与批量爬取行为。
这一层是传统数据安全资料里讲得最多的部分,但对于微服务架构而言,只需要守住边界即可,不需要过度设计。
4.2 应用层:身份与权限的精准管控
应用层是跨租户授权的核心执行层,目标是身份可验证、权限可控制。
- 身份映射:统一认证中心支持多租户身份映射,只做授权关联,不做账号打通,双方账号体系保持独立;
- 双维度鉴权:资源接口必须做「租户标识 + 资源范围」双维度校验,只校验令牌有效性、不校验租户归属是最常见的漏洞;
- 令牌约束:访问令牌必须绑定租户标识、授权范围与有效期,禁止跨租户复用、禁止超范围使用。
这一层完全可以复用我们之前 Spring Security、Spring Authorization Server 的技术栈,只是增加了租户维度的校验逻辑。
4.3 数据层:最后一道兜底防线
数据层是最后一道防线,目标是即使权限出现漏洞,数据也不会大面积泄露。
- 分级分类:数据按租户、按敏感等级打标,不同等级的数据采用不同的授权模式,高敏感数据禁止使用直访模式;
- 强制脱敏:跨租户共享的数据必须经过脱敏处理,敏感字段做不可逆替换或掩码;
- 行级隔离:即使授权访问,也只能访问授权范围内的行数据,禁止整表访问;
- 溯源水印:对输出的数据嵌入不可见数字水印,出现泄露时可以追溯源头。
这一层和我们之前讲的行级权限、差分隐私是同一套体系,只是从单租户内部访问,延伸到了跨租户共享场景。
4.4 治理层:全生命周期的安全管控
治理层是体系能否长期运转的关键,目标是授权全流程可控、可查、可回收。
- 审批流程:跨租户授权必须走正式审批流程,禁止技术图省事直接开通;
- 全链路审计:申请、审批、访问、操作全链路留痕,定期审计授权合理性;
- 生命周期管理:授权到期自动失效,定期清理闲置授权,避免 “僵尸授权”;
- 应急机制:异常访问自动熔断,支持一键回收全部权限,应对安全事件。
五、选型决策与工程避坑
5.1 选型决策矩阵
四种模式没有好坏,关键是匹配场景。整理一个快速决策表,方便工程选型:
场景特征 | 优先选择模式 | 核心原因 |
高实时性、中低敏感度、访问频繁 | 授权直访模式 | 无冗余、延迟低,体验最好 |
低实时性、B 侧需二次加工、访问量很大 | 数据同步模式 | 本地访问性能好,不依赖源服务 |
租户数量多、多对多授权、需统一治理 | 代理中转模式 | 统一收口,降低管理复杂度 |
高敏感度、统计分析、强合规要求 | 结果输出模式 | 数据不出域,安全等级最高 |
5.2 工程避坑指南
这些都是实际项目里踩过的坑,也是最容易出问题的地方:
- 权限过度开放为了联调方便,一开始就给全量权限、永久有效期,后续再也收不回来。一定要坚持最小权限原则,先给最小范围,不够再追加。
- 同步数据残留授权撤销后,只停了同步任务,已经同步过去的数据不清理。必须建立强制清理机制,撤销后限期删除,并且做校验。
- 令牌无租户绑定资源接口只校验令牌签名是否合法,不校验令牌归属的租户,导致一个租户的令牌可以访问另一个租户的数据。这是非常高发的低级漏洞。
- 忽略审计链路只做授权,不做审计。出了数据泄露事件,查不到谁申请的、谁审批的、谁访问的。
- 模式错配高敏感数据图省事用直访模式,等于把数据直接暴露在对方面前。敏感数据宁可牺牲一些灵活性,也要优先保证安全。
六、总结与核心复习要点
6.1 总结
回到最开始的问题:微服务安全体系,单租户看纵深,多租户看横向。 前两篇文章构建了单租户内部的五层纵深防御,沿着请求链路层层把关;这一篇补上了多租户场景下的横向授权体系,沿着租户边界做权限管控与数据防护。纵向纵深 + 横向授权,合起来就是一套完整的企业级微服务安全框架。
我一直认为,安全从来不是单点技术的堆砌,而是体系化的设计。跨租户授权的核心从来不是 “怎么把数据打通”,而是 “怎么在可控的边界内打通”。我们不是追求绝对安全,也不是追求极致效率,而是在业务需求、安全风险、落地成本之间找到那个最合适的平衡点。
6.2 核心复习要点
- 本质认知:跨租户授权是使用权的限时开放,不是所有权转移;核心三原则:最小权限、全程可审计、权限可回收。
- 四种模式:授权直访(实时无冗余)、数据同步(本地高性能)、代理中转(统一治理)、结果输出(最高安全)。
- 技术体系:网络层做边界隔离,应用层做身份权限,数据层做兜底防护,治理层做全生命周期管控。
- 选型逻辑:高实时选直访、多加工选同步、多租户选中台、高敏感选结果。
- 避坑核心:权限最小化、数据可回收、全链路可审计。
📚 我的技术博客导航:[点击进入一站式查看所有干货]