通过Aop实现限制修改删除指定账号的数据
1、需求
对于Teach账号创建的数据,其他用户仅仅只有查询的权限,而不能修改和删除。并且部分接口只允许Teach账号访问
2、实现思路
在删除和修改时往往需要传递数据的id,进而可以通过id查询该数据是否由Teach账号创建。当然我们可以在每个删除和修改接口里面进行判断,但这样照成管控不够集中,并且不利于后续的修改。如果后续又要求对Admin账号创建的数据也做同样的管控,那需要修改的地方会很多。
因此可以通过Aop拦截删除、修改等接口,通过接口的参数中的查询id对应的数据是否由Teach账号创建。
3、实现Demo
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.function.BiConsumer;
@Order(0)
@Aspect
@Component
public class TeachAccountCheckAop {
private static final Logger logger = LoggerFactory.getLogger(TeachAccountCheckAop.class);
@Autowired
private HttpServletRequest request;
@Autowired
private UserInfoUtil userInfoUtil;
@Autowired
private ExpInfoService expInfoService;
@Autowired
private ExpManualService expManualService;
@Autowired
private MaterialInfoService materialInfoService;
@Autowired
private SandboxMgtService sandboxMgtService;
/**
* 存储每个接口对应的从接口参数中获取id的方式
*/
private Map<String, BiConsumer<Object, List<Long>>> classMap = new HashMap<>();
/**
* 存储仅仅运行Teach账号访问的接口
*/
private Set<String> onlyTeachApiMethodSet = new HashSet<>();
{
onlyTeachApiMethodSet.add("com.huawei.ebg.olblab.controller.ExpInfoController.exportExperiment");
onlyTeachApiMethodSet.add("com.huawei.ebg.olblab.controller.ExpInfoController.importExperiment");
/**
* arg:接口的参数
* list:用于存储从接口参数中提取的id
*/
classMap.put("com.huawei.ebg.olblab.controller.ExpInfoController.saveExpBaseInfo", (arg, list) -> {
ExpBaseInfoDto param = (ExpBaseInfoDto) arg;
if (Objects.nonNull(param.gettid())) {
list.add(param.gettid());
}
});
classMap.put("com.huawei.ebg.olblab.controller.ExpManualController.saveExpManualBaseInfo", (arg, list) -> {
ExpManualBaseDto param = (ExpManualBaseDto) arg;
if (Objects.nonNull(param.gettid())) {
list.add(param.gettid());
}
});
classMap.put("com.huawei.ebg.olblab.controller.ExpMaterialInfoController.updateExpMaterialInfo", (arg, list) -> {
MaterialUpdateDto param = (MaterialUpdateDto) arg;
if (Objects.nonNull(param.gettid())) {
list.add(param.gettid());
}
});
classMap.put("com.huawei.ebg.olblab.controller.SandboxMgtController.deleteSandbox", (arg, list) -> {
SandboxTempDto.Delete param = (SandboxTempDto.Delete) arg;
if (CollectionUtils.isNotEmpty(param.getSandboxTempIds())) {
list.addAll(param.getSandboxTempIds());
}
});
}
@Before("execution(public * com.huawei.ebg.olblab.controller.ExpInfoController.saveExpBaseInfo(..))"
+ "|| execution(public * com.huawei.ebg.olblab.controller.ExpManualController.saveExpManualBaseInfo(..))"
+ "|| execution(public * com.huawei.ebg.olblab.controller.ExpMaterialInfoController.updateExpMaterialInfo(..))"
+ "|| execution(public * com.huawei.ebg.olblab.controller.SandboxMgtController.deleteSandbox(..))")
public void beforeApiCheck(JoinPoint joinPoint) {
// 获取当前登录者账号,Teach账号直接结束
String userAccount = userInfoUtil.getUserAccount(request);
if (StringUtils.equals(userAccount, ImportAccount.TEACH)) {
return;
}
// 获取切点的方法
String proxyTargetClass = joinPoint.getSignature().getDeclaringTypeName();
String proxyTargetMethod = proxyTargetClass + "." + joinPoint.getSignature().getName();
// 检查接口是否仅Teach能访问
if (onlyTeachApiMethodSet.contains(proxyTargetMethod)) {
if (StringUtils.equals(userAccount, ImportAccount.CREAT)) {
return;
}
throw new RuntimeException(ErrorCode.EDU81080); // 仅Teach能访问
}
// 从接口参数中获取id
Object[] args = joinPoint.getArgs();
BiConsumer<Object, List<Long>> consumer = classMap.get(proxyTargetMethod);
List<Long> ids = new ArrayList<>();
for (Object arg : args) {
if (Objects.isNull(arg)) {
continue;
}
consumer.accept(arg, ids);
}
if (CollectionUtils.isEmpty(ids)) {
return;
}
// 不同controller的接口,调用不同的Service查询id对应的数据的创建人是否存在Teach账号创建的数据
long count = 0L;
if (StringUtils.equals(proxyTargetClass, ExpInfoController.class.getName())) {
count = expInfoService.count(Wrappers.lambdaQuery(ExpInfoEntity.class)
.eq(ExpInfoEntity::getCreateBy, ImportAccount.TEACH)
.in(ExpInfoEntity::getType, ExpType.TYPE_0, ExpType.TYPE_1)
.in(ExpInfoEntity::gettid, ids));
} else if (StringUtils.equals(proxyTargetClass, ExpManualController.class.getName())) {
count = expManualService.count(Wrappers.lambdaQuery(ExpManualEntity.class)
.eq(ExpManualEntity::getCreateBy, ImportAccount.TEACH)
.in(ExpManualEntity::gettid, ids));
} else if (StringUtils.equals(proxyTargetClass, ExpMaterialInfoController.class.getName())) {
count = materialInfoService.count(Wrappers.lambdaQuery(MaterialInfoEntity.class)
.eq(MaterialInfoEntity::getCreateBy, ImportAccount.TEACH)
.in(MaterialInfoEntity::gettid, ids));
} else if (StringUtils.equals(proxyTargetClass, SandboxMgtController.class.getName())) {
count = sandboxMgtService.count(Wrappers.lambdaQuery(SandboxTempEntity.class)
.eq(SandboxTempEntity::getCreatedByName, ImportAccount.TEACH)
.in(SandboxTempEntity::getSandboxTempId, ids));
}
if (count > 0) {
throw new RuntimeException(ErrorCode.EDU81080); // 仅Teach能访问
}
}
}
4、可优化点
切入点表达式,可以改为自定义注解