多租户 GEO 优化系统源码架构:权限隔离与数据分离实现

1. 引言

多租户 GEO 优化系统在服务多个业务方时,需要同时解决两个核心问题:一是租户之间的权限隔离,确保每个租户只能访问自己的数据和功能;二是数据分离,避免租户数据互相污染。本文从源码架构角度,分析权限隔离与数据分离的具体实现方案。

2. 整体架构设计

系统采用分层架构,从上到下依次为接入层、服务层、数据访问层和存储层。权限隔离贯穿接入层和服务层,数据分离则主要在数据访问层和存储层落地。

text 复制代码
接入层(API Gateway / 租户上下文解析)
        ↓
服务层(业务服务 / 权限校验拦截器)
        ↓
数据访问层(多租户数据路由 / 数据源路由)
        ↓
存储层(共享库共享表 / 共享库独立表 / 独立库)

3. 权限隔离实现

3.1 租户上下文传递

租户标识通过请求头或 Token 解析后,写入线程上下文,供后续业务逻辑和拦截器使用。

java 复制代码
public class TenantContext {
    private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        TENANT_ID.set(tenantId);
    }

    public static String getTenantId() {
        return TENANT_ID.get();
    }

    public static void clear() {
        TENANT_ID.remove();
    }
}

3.2 拦截器校验

在服务层入口统一校验租户权限,未携带合法租户标识的请求直接拒绝。

java 复制代码
public class TenantAuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String tenantId = request.getHeader("X-Tenant-Id");
        if (tenantId == null || tenantId.isEmpty()) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        TenantContext.setTenantId(tenantId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        TenantContext.clear();
    }
}

3.3 接口级权限控制

对于敏感操作,在方法级别通过注解或配置进一步校验租户角色权限。

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantPermission {
    String[] roles() default {};
}

4. 数据分离实现

4.1 共享库共享表

所有租户共用同一张表,通过 tenant_id 字段区分数据归属。该方案成本最低,适合租户数据量小、隔离要求不高的场景。

sql 复制代码
CREATE TABLE geo_optimization_task (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id VARCHAR(64) NOT NULL,
    task_name VARCHAR(128) NOT NULL,
    geo_config TEXT,
    status TINYINT DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    KEY idx_tenant_id (tenant_id)
);

4.2 数据访问层自动拼接租户条件

通过 MyBatis 拦截器或 JPA 过滤器,在 SQL 执行前自动追加 tenant_id 条件,避免业务代码重复编写。

java 复制代码
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class TenantSqlInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        String tenantId = TenantContext.getTenantId();
        if (tenantId != null && !sql.contains("tenant_id")) {
            String newSql = injectTenantCondition(sql, tenantId);
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, newSql);
        }
        return invocation.proceed();
    }
}

4.3 共享库独立表

每个租户拥有独立的表,表名带租户前缀,数据物理隔离,但共享同一个数据库实例。适合租户数据量较大、需要独立备份的场景。

java 复制代码
public class TenantTableRouter {
    public String resolveTableName(String baseTableName, String tenantId) {
        return tenantId + "_" + baseTableName;
    }
}

4.4 独立数据库

每个租户独占一个数据库,隔离级别最高,适合对数据安全要求严格的租户。通过动态数据源路由实现。

java 复制代码
public class TenantDataSourceRouter extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getTenantId();
    }
}

5. 缓存与文件存储隔离

Redis 缓存键统一加上租户前缀,文件存储路径按租户目录隔离,防止跨租户数据泄露。

java 复制代码
public class TenantCacheKeyBuilder {
    public static String build(String key) {
        return TenantContext.getTenantId() + ":" + key;
    }
}

6. 源码目录结构

text 复制代码
geo-optimization-system/
├── gateway/                 # 接入层,租户上下文解析
├── common/                  # 公共模块,租户上下文、拦截器
├── service/                 # 业务服务
│   ├── task/                # 优化任务服务
│   └── permission/          # 权限校验服务
├── dal/                     # 数据访问层
│   ├── interceptor/         # SQL 租户条件注入
│   └── router/              # 数据源与表路由
└── storage/                 # 存储层,缓存与文件隔离

7. 总结

多租户 GEO 优化系统的权限隔离与数据分离,核心在于租户上下文的统一传递、拦截器的集中校验,以及数据访问层的自动路由。实际项目中应根据租户规模、数据敏感度和成本要求,灵活选择共享表、独立表或独立库方案,并在缓存和文件存储层面同步落实隔离策略。

相关推荐
零基础12314 分钟前
PDF 处理工具全攻略:pdf24、PDFgear、Adobe Acrobat DC 与 Stirling-PDF 深度对比
java·开发语言·pdf
SL_staff28 分钟前
制造业数字化协同:为什么甘特图只是起点,不是终点
java·开源·全栈
imDwAaY31 分钟前
Spring中过滤器和拦截器的区别是什么?
spring boot·后端·spring
anew___1 小时前
《从零手写操作系统 (29):管道与重定向进阶——命名管道、Here Document与Shell语法扩展》
java·开发语言·前端·javascript·网络
Mikko71 小时前
JVM 线上排查实战(六):JFR 怎么用?JDK 8 要不要加 UnlockCommercialFeatures、jfr 命令在哪、录的文件为什么读不了
java·运维·jvm·后端
奇牙coding1 小时前
GPT-5.5 API 报 401 但 GPT-5.4 正常怎么办?不是 Key 失效,是 Organization 头的强制校验变了
java·网络·gpt·ai
a努力。1 小时前
Context-State-Memory三重信息架构揭秘
java·服务器·前端
桃西西呀2 小时前
Spring AI Alibaba 之二:Spring AI 底座与原子抽象一笔带过,看清 SAA 在之上加了什么编排
人工智能·spring·llm
captain3762 小时前
Maven
java·后端·idea