主要问题:
如何禁止从 Controller 直接呼叫 Repository
问题描述:
我使用 spring 来开发 webapp
采用三层式架构
Controller -> Service -> Repository
// Repository 是 Spring Data 使用的术语,可以想像成 DAO
原本打算把 jta, auditing log 加在 Service layer 上
所以我不希望其他开发人员从 Controller 这里直接呼叫 Repository
但是其他开发人员不可能被我控制
他们可以随意的在 controller 里面任意 autowired repository
接下来就随便他们搞了
后来我加了一个 AOP 来限制 repository 的呼叫
如果是从 service package 呼叫的就放行,否则抛出例外
@Aspect
public class ModelAdvice {
private Pattern pattern = Pattern.compile("^demo\\.services\\..*");
@Before("execution(* demo.repositories..*Repository.*(..))")
public void protectRepositories(JoinPoint joinPoint) {
StackTraceElement[] stElements =
Thread.currentThread().getStackTrace();
for (StackTraceElement element : stElements) {
Matcher matcher = pattern.matcher(element.getClassName());
if (matcher.matches()) {
return;
}
}
throw new RuntimeException("security violation!");
}
}
虽然可以动,但是觉得不太漂亮,而且可能会很慢
后来有人说把 service 跟 repository 放在同一个 package
然后 repository 定义成 default 这样子的话
controller 完全无法定义 repository 型别的变量
看起来似乎很有道理,但是很快又被破解了
@Autowired
private ApplicationContext context;
private JpaRepository getRepoBean(String name) {
try {
Class<?> formTypeClazz = Class.forName(name);
Object bean = context.getBean(formTypeClazz);
JpaRepository repo = (JpaRepository)bean;
return repo;
}
catch (BeansException e) {
throw new RuntimeException();
}
catch (ClassNotFoundException e) {
throw new RuntimeException();
}
}
只要把想要用的 repository qualified class name 传进来就能拿到
虽然是 JpaRepository 但是基本功能都有
而且前面的 AOP 似乎拦不到
请问各位先进,是否有更好的方法?