我想通过使用Spring Boot AOP实现授权方法。最初的想法是,如果从REST调用返回的返回对象未通过授权检查,则会抛出未授权的异常。
我正在这样做:
@Aspect
@Component
public class AuthAspect {
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
但是,问题是,如果此REST服务将对我的数据库执行一些INSERT或UPDATE,它将在我的授权检查之前提交。因此,UnauthException
将被抛出,但事务仍将提交。
我想在proceed()
调用之前手动创建事务并在返回之前提交它的第一次尝试,但是失败了。
@Aspect
@Component
public class AuthAspect {
private final EntityManager em;
@Autowired
public AuthAspect(EntityManager em) {
this.em = em;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
em.getTransaction().begin();
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
em.getTransaction().commit();
return returnObject;
}
}
这将导致java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
。
我在互联网上搜索了一些答案,需要修改web.xml
文件,但是我不想使用xml进行配置。
答案 0 :(得分:1)
从标签来看,您正在使用Spring Boot。 Spring Boot提供了一个预先配置的TransactionTemplate
,如果您要手动控制事务,可以使用它。
代替EntityManger
将其注入您的方面并将代码包装在其中。
@Aspect
@Component
public class AuthAspect {
private final TransactionTemplate tx;
public AuthAspect(TransactionTemplate tx) {
this.tx = tx;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint pjp) throws Throwable {
return tx.execute(ts -> this.executeAuth(pjp));
}
private Object executeAuth(ProceedingJoinPoint pjp) {
Object returnObject;
try {
returnObject = pjp.proceed();
} catch (Throwable t) {
throw new AopInvocationException(t.getMessage(), t);
}
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
这将在事务内执行逻辑。我将实际的逻辑移到一个方法上,以便lambda可以是单个方法而不是代码块。 (个人偏好/最佳实践)。
答案 1 :(得分:0)
看代码,您需要更新几件事:
如果要调用EntityManager,则需要在持久性上下文中使用以下方法进行调用。
@PersistenceContext
private EntityManager em;
为了在事务上下文中执行查询或其他操作,您只需要在方法标头上添加Transactional标签,而不必从EntityManager调用getTransaction。
@Transactional
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
em.createNativeQuery(query) //If it is necessary.
...
}