能否通过自定义Aspect为Spring Data的全操作追加动态where子句?
问题原因说明
你当前使用的Hibernate Session Filter默认仅对SELECT查询生效,对于批量UPDATE/DELETE、以及直接调用的EntityManager.remove()、Spring Data自带的delete/update方法默认不会触发Filter的条件拼接,这是Hibernate本身的设计,所以你现在的AOP虽然已经启用了Filter,但修改操作不会带过滤条件。
可行解决方案
方案1:启用Hibernate Filter对DML操作的支持(适配现有代码成本最低)
如果你的Hibernate版本在5.4及以上,可以直接通过配置开启Filter对修改操作的支持,不需要改动现有Aspect代码:
# application.properties 配置 spring.jpa.properties.hibernate.apply_filters_to_sql_dml=true
开启后你定义的authorize过滤器会自动拼接在批量UPDATE、DELETE语句的WHERE条件后面,和查询逻辑保持一致。
注意:如果你是先查询实体、再修改属性调用save(),或者调用
delete(实体对象)的场景,本身查询阶段已经触发了行级过滤,你现有的代码已经能控制权限,只有直接执行批量JPQL/SQL的修改删除才需要开启这个配置。
方案2:扩展AOP逻辑拦截修改操作(适配低版本Hibernate)
如果Hibernate版本低于5.4,不支持上面的配置,可以扩展你的AOP切点,单独处理带@Modifying注解的修改方法:
- 对于按ID删除/更新的操作:先查询对应ID的实体是否符合当前用户的行级权限,存在才允许执行,不存在直接抛出权限异常
- 对于批量更新/删除操作:拦截方法的JPQL语句,手动拼接行级过滤的WHERE条件后再执行
示例扩展代码:
// 新增Modifying方法切点 @Pointcut("@annotation(org.springframework.data.jpa.repository.Modifying)") public void modifyingMethod() {} @Around("ownerAwareRepositoryMethod() && modifyingMethod()") public Object aroundModifying(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Query queryAnn = signature.getMethod().getAnnotation(Query.class); String originSql = queryAnn.value(); String branchId = getBranchId(); String newSql; // 拼接行级过滤条件,区分有没有原WHERE子句的场景 if (originSql.toUpperCase().contains("WHERE")) { newSql = originSql.replaceFirst("(?i)where", "WHERE branch_id = :branchId AND "); } else { newSql = originSql + " WHERE branch_id = :branchId"; } // 构造新查询执行 Query query = entityManager.createQuery(newSql); query.setParameter("branchId", branchId); // 补全方法入参 Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { query.setParameter(i+1, args[i]); } return query.executeUpdate(); }
方案3:用Hibernate StatementInspector全局拦截SQL(最通用)
如果需要适配所有场景的SQL修改(包括原生SQL、ORM自动生成的SQL),可以实现Hibernate的StatementInspector接口,全局拦截所有执行的SQL动态拼接过滤条件:
@Component public class RLSStatementInspector implements StatementInspector { @Override public String inspect(String sql) { String upperSql = sql.trim().toUpperCase(); // 仅处理修改类操作 if (upperSql.startsWith("UPDATE") || upperSql.startsWith("DELETE")) { // 判断操作的表是否需要行级过滤(自行实现对应逻辑,比如判断表是否标注了@Filter注解) if (isNeedFilterTable(sql)) { String branchId = SecurityUtils.getAuthenticatedUser().getBranch().getId(); if (upperSql.contains("WHERE")) { return sql.replaceFirst("(?i)where", "WHERE branch_id = '" + branchId + "' AND "); } else { return sql + " WHERE branch_id = '" + branchId + "'"; } } } return sql; } private boolean isNeedFilterTable(String sql) { // 实现表匹配逻辑 } }
配置启用Inspector:
spring.jpa.properties.hibernate.session_factory.statement_inspector=你的包路径.RLSStatementInspector
注意事项
- 所有方案都需要额外处理没有WHERE子句的UPDATE/DELETE语句,避免SQL拼接错误
- 建议配合权限校验兜底,避免过滤条件失效导致数据误改
内容的提问来源于stack exchange,提问作者elias-heydarpour
相关产品推荐
相关产品推荐

