当前位置: 首页 > news >正文

通过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、可优化点

切入点表达式,可以改为自定义注解

相关文章:

  • 哈尔滨网络营销推广软件抖音seo排名优化
  • 网站大全免费完整版微信广告投放推广平台多少费用
  • 手机网站开发成本企业信息查询
  • 做外贸的网站有何用处东莞seo排名公司
  • 做的网站图片不显示有域名和服务器怎么建网站
  • 蒙特网站建设公司网络流量分析工具
  • QML Loader:动态加载与控件创建
  • git命令
  • LLM面试题十
  • EmotiVoice 易魔声AI语音下载安装使用教程​(附安装包)
  • 移动端、PC端(Web) 和 桌面端(客户端应用)AI驱动测试实现方案
  • Spring-MVC
  • 从C语言到Go语言:新手快速入门指南
  • 解析 LILIkoi 光纤力传感器:FBG 原理铸就耐高温抗干扰优势
  • python-Leetcode 65.搜索旋转排序数组
  • 知识表示方法之六:过程表示法(Procedural Representation)
  • 【AI】prompt engineering
  • 组播网络构建:IGMP、PIM 原理及应用实践
  • Java Arrays工具类详解
  • 2025 年福建交安安全员考试:结合本省交通特点备考​
  • 大模型ui设计SVG输出
  • STM32——I2C通讯(软件模拟)
  • 解决 Jetpack Compose 中 State 委托报错:“no method getValue“ 的终极指南
  • 通用接口函数注册模块设计与实现
  • virt-manager配置NAT
  • PPIO × UI-TARS:用自然语言操控电脑,AI Agent 的极致体验