一、权限元数据建模

  1. 权限维度抽象
  • 建立数据权限矩阵表(data_permission_matrix)
CREATE TABLE data_permission_rule (
    id BIGINT PRIMARY KEY,
    role_code VARCHAR(32) NOT NULL, -- 角色标识
    resource_type VARCHAR(64) NOT NULL, -- 资源类型(如订单、客户)
    condition_type ENUM('SELF','DEPT','CUSTOM') NOT NULL, -- 条件类型
    condition_expression VARCHAR(512) -- SQL条件表达式
);
  1. 用户上下文封装
public class UserContext {
    private Long userId;
    private String deptId;
    private Set<String> roles;
    // 附加属性:岗位、区域等
}

二、动态条件拦截器

  1. MyBatis拦截器实现
@Intercepts({@Signature(type = Executor.class, method = "query", 
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class DataPermissionInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 解析当前用户上下文
        UserContext user = SecurityUtils.getCurrentUser();
        
        // 2. 获取Mapper绑定的资源类型
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        String resourceType = parseResourceType(ms.getId());
        
        // 3. 查询权限规则集
        List<DataPermissionRule> rules = ruleService.getRules(user.getRoles(), resourceType);
        
        // 4. 构建动态WHERE条件
        String dynamicCondition = buildCondition(rules, user);
        
        // 5. 改写原始SQL
        BoundSql boundSql = ms.getBoundSql(invocation.getArgs()[1]);
        String newSql = boundSql.getSql() + " AND " + dynamicCondition;
        
        // 6. 创建新BoundSql继续执行
        // ... 具体实现省略
    }
}

三、规则引擎设计

  1. 条件表达式解析器
public class ConditionParser {
    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{(\\w+)\\}");

    public static String parse(String template, UserContext user) {
        Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = getValueFromUser(key, user);
            matcher.appendReplacement(sb, value);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    
    private static String getValueFromUser(String key, UserContext user) {
        switch (key) {
            case "userId": return user.getUserId().toString();
            case "deptId": return user.getDeptId();
            case "roleCodes": return String.join(",", user.getRoles());
            default: throw new IllegalArgumentException("未知占位符: " + key);
        }
    }
}

四、服务层整合

  1. AOP权限校验增强
@Aspect
@Component
public class DataPermissionAspect {
    
    @Pointcut("@annotation(com.example.anno.DataPermission)")
    public void dataPermissionPointcut() {}
    
    @Around("dataPermissionPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 解析方法注解获取资源类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        DataPermission annotation = signature.getMethod().getAnnotation(DataPermission.class);
        
        // 2. 将资源类型存储到ThreadLocal
        DataPermissionContext.setResourceType(annotation.value());
        
        try {
            return joinPoint.proceed();
        } finally {
            DataPermissionContext.clear();
        }
    }
}

五、多租户扩展设计

  1. 租户隔离方案
  • 通过TenantID自动注入
public class TenantContext {
    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();

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

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

// 在拦截器中自动追加条件
String tenantCondition = "tenant_id = '" + TenantContext.getTenantId() + "'";

六、缓存优化策略

  1. 规则缓存设计
@Cacheable(value = "permissionRules", key = "#roleCodes.concat(#resourceType)")
public List<DataPermissionRule> getRules(Set<String> roleCodes, String resourceType) {
    // 数据库查询逻辑
}

实施建议:

  1. 渐进式实施路径

    • Phase1:硬编码基础过滤条件
    • Phase2:动态SQL改写基础版
    • Phase3:完整规则引擎实现
    • Phase4:可视化规则配置界面
  2. 监控指标

@Data
public class DataPermissionMetrics {
    private int sqlRewriteCount;  // SQL改写次数
    private int ruleHitCount;     // 规则命中次数
    private Map<String, Integer> resourceAccessStats; // 资源访问统计
}
  1. 防御性编程要点
    • SQL注入防护:对用户输入参数严格校验
    • 兜底策略:默认开启全量数据保护模式
    • 审计日志:记录原始SQL与改写后SQL

该方案通过多层级联动控制,实现了从用户属性到SQL语句的动态转换,既保证了权限控制的灵活性,又维持了系统性能的稳定性。建议配合Apollo等配置中心实现规则的动态热更新,并定期进行权限矩阵验证测试。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐