SpringAOP切面实例实现对数据过滤返回,SpringAOP切面实现对用户权限控制,通过@Around注解过滤修改方法返回值

news2025/1/11 5:07:37

文章目录

        • 需求内容:
      • 实现:
          • 步骤一:导入SpringAOP相关依赖pom.xml
          • 步骤二:自定义两个注解
          • 步骤三:需要用到的实体类
          • **步骤四:切面具体实现**
      • 用法
          • 1.需要过滤返回值的方法添加注解@FilterByUser
          • 2.数据Dto在需要过滤的字段添加@Filter注解,值为数据库中json字段的key
          • 3.数据库中添加一条记录
          • 4.完成配置的效果
      • **实现原理描述**

需求内容:

在系统已经完成的情况下,添加以下权限:

·城市为“上海”和“深圳”的“部门一”用户,只能看到用户表数据中城市为“上海或深圳”且部门为“部门一的子部门”。

所用技术包含,自定义注解,SpringAOP切面,反射以及其他SpringBoot项目常用

实现:

步骤一:导入SpringAOP相关依赖pom.xml
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.6.6</version>
        </dependency>
步骤二:自定义两个注解
package cn.fy.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Fy
 * 自定义注解,在实体类中有该注解的字段即可以被过滤
 * @Date 2022年12月14日 11:12:59
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Filter {

    //{"bumen":["部门11","部门12","部门13","部门14"]} 则keyName为 bumen
    //此处用“bumen”只是为了证明可以和实体类的dept不同
    String value() default "";


}

package cn.fy.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterByUser {

}

步骤三:需要用到的实体类

1.要查询要过滤的数据实体类,需继承2实体类,或(包含2实体类中需要查询的字段,并修改对应切面中获取前端传递用户账号的方法)

package cn.fy.dto;

import cn.fy.anno.Filter;
import lombok.Data;
import java.io.Serializable;
@Data
public class User extends QueryDto implements Serializable {
    private String userName;
    @Filter("bumen")
    //写成拼音只是为了证明可以与实体类字段名不一致
    private String dept;
    @Filter("chengshi")
    private String city;
}

2.接收前端传递参数的查询实体类

package cn.fy.dto;
import lombok.Data;
@Data
public class QueryDto {
    int pageIndex;
    int pageSize;
    String role;
    //登录的用户账号
    String userName;
}

3.权限数据库表对应的实体类和对应Mapper

package cn.fy.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 * @author Fy
 * @since 2023-02-10
 */
@Data
@TableName("user_power")
public class UserPower implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId("id")
    private Integer id;
    @TableField("user_name")
    private String userName;
    @TableField("create_time")
    private String createTime;
    @TableField("json")
    private String json;
}

package cn.fy.sql;

import cn.fy.dto.UserPower;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Fy
 * @since 2023-02-10
 */
@Mapper
public interface UserPowerMapper extends BaseMapper<UserPower> {

}
步骤四:切面具体实现
package cn.fy.aspect;

import cn.fy.anno.Filter;
import cn.fy.dto.UserPower;
import cn.fy.dto.QueryDto;
import cn.fy.sql.UserPowerMapper;
import cn.fy.sql.UserPowerMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@Component
@Aspect
@Slf4j
public class PowerAspect {

    @Resource
    private UserPowerMapper userPowerMapper;

    //需要限定包的话则自行添加@exectution
    @Pointcut("@annotation(cn.fy.anno.FilterByUser)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object reDto = null;
        Object[] args = joinPoint.getArgs();
        try {
            //执行方法并获得返回值
            reDto = joinPoint.proceed(args);
        } catch (Throwable throwable) {
            log.error("", throwable);
            throw new RuntimeException(throwable);
        }

        QueryDto queryDto = null;
        for (Object arg : args) {
            if (arg instanceof QueryDto) {
                queryDto = (QueryDto) arg;
                break;
            }
        }
        if (queryDto == null) {
            return reDto;
        }

        //此部分为MybatisPlus查询数据库方法,可自行替换
        LambdaQueryWrapper<UserPower> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPower::getUserName, queryDto.getUserName());
        UserPower powerDto = userPowerMapper.selectOne(queryWrapper);


        log.info(JSON.toJSONString(powerDto));
        log.info("---查询时间戳---" + System.currentTimeMillis());
        JSONObject jsonObject = null;
        if (powerDto == null) {
            return reDto;
        }
        try {
            jsonObject = JSON.parseObject(powerDto.getJson());
        } catch (Exception e) {
            log.error("", e);
        }
        if (jsonObject == null) {
            return reDto;
        }
        try {
            if (reDto instanceof List) {
                //是集合
                List list = (List) reDto;
                List successList = new ArrayList();
                //循环整个集合
                for (Object o : list) {
                    Field[] fields = o.getClass().getDeclaredFields();
                    List<Boolean> booleanList = new ArrayList<>();
                    for (Field field : fields) {
                        field.setAccessible(true);
                        Filter annotation = field.getAnnotation(Filter.class);
                        if (annotation != null) {
                            String keyName = annotation.value();
                            Object o1 = jsonObject.get(keyName);
                            //如果获取到了key的话
                            if (!ObjectUtils.isEmpty(o1)) {
                                List list1 = (List) o1;
                                //如果当前记录在记录中的话
                                booleanList.add(list1.contains(field.get(o)));
                                if (list1.contains(field.get(o))) {
                                    log.info("key为" + keyName + ":的值【" + field.get(o) + "】在配置的权限中");
                                }
                            }
                        }
                    }
                    if (!booleanList.contains(false)) {
                        //证明这个数据是对的
                        successList.add(o);
                    }
                }
                return successList;
            } else {
                return reDto;
            }
        } catch (Exception e) {
            log.error("", e);
        }
        return reDto;
    }


}



用法

1.需要过滤返回值的方法添加注解@FilterByUser

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2o1ptGjs-1676532488414)(C:\Users\song.cai\AppData\Roaming\Typora\typora-user-images\1676531140607.png)]

2.数据Dto在需要过滤的字段添加@Filter注解,值为数据库中json字段的key

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EKD75W9x-1676532488415)(C:\Users\song.cai\AppData\Roaming\Typora\typora-user-images\1676531216000.png)]

3.数据库中添加一条记录

在这里插入图片描述

4.完成配置的效果

原输出结果

[User(userName=user163122156, dept=部门11, city=北京), 
User(userName=user163122156, dept=部门11, city=上海), 
User(userName=user163122156, dept=部门2, city=上海), 
User(userName=user163122156, dept=部门11, city=深圳)]

加过滤之后输出结果

[User(userName=user163122156, dept=部门11, city=上海)]

实现原理描述

利用@Aspect注解来对切面进行编写,通过注解形式的切入点表达式,对加了@FilterByUser注解的方法进行过滤。利用@Around注解过滤修改原方法的返回值,在切面中通过反射获取原方法返回实体类中加了@Filter注解的字段,通过去查询数据库对该实体类中该字段的值进行比较过滤,多个@Filter需要全部校验通过才放行该对象,否则直接过滤掉不展示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/350065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树与二叉树 总复习

一、树的定义 树是一个有n个&#xff08;n>0&#xff09;结点的有限集合。 如果n0&#xff0c;称为空树&#xff1b; 如果n>0&#xff0c;称为非空树&#xff0c;有且仅有一个特定的称为根Root的结点&#xff08;无直接前驱&#xff09; 如果n>1,除了根节点外&…

总结高频率Vue面试题

目录 什么是三次握手&#xff1f; 什么是四次挥手&#xff1f;&#xff08;close触发&#xff09; 什么是VUEX&#xff1f; 什么是同源----跨域&#xff1f; 什么是Promise&#xff1f; 什么是fexl布局&#xff1f; 数据类型 什么是深浅拷贝&#xff1f; 什么是懒加载&…

HTB打靶(Active Directory 101 Multimaster)

Nmap扫描 Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-08 02:52 EST Stats: 0:00:51 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan SYN Stealth Scan Timing: About 55.85% done; ETC: 02:54 (0:00:40 remaining) Nmap scan report for 10.129…

AcWing语法基础课笔记 第四章 C++中的数组

第四章 C中的数组 程序 逻辑 数据&#xff0c;数组是存储数据的强而有力的手段。 ——闫学灿 一维数组 数组的定义 数组的定义方式和变量类似。 数组的初始化 在main函数内部&#xff0c;未初始化的数组中的元素是随机的。 访问数组元素 通过下标访问数…

【正点原子FPGA连载】第十三章QSPI Flash读写测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十三章QSPI Fl…

异构数据库同步方案

目录 1 概述 2 原理 3 参数 1 概述 将企业生产系统产生的业务数据实时同步到大数据平台&#xff0c;通过对业务数据的联机实时分析&#xff0c;快速制定或调整商业计划&#xff0c;提升企业的核心竞争力。 依据同步数据是否需要加工处理&#xff0c;采用不同的技术方案&am…

网页概念、常用浏览器及内核、Web标准

网页、常用浏览器及内核、Web标准一、网页1.1、什么是网页&#xff1f;1.2、什么是HTML?&#xff08;重点&#xff09;1.3、网页的形成&#xff1f;二、常用浏览器三、浏览器内核四、Web标准&#xff08;重点&#xff09;4.1 为什么需要Web标准&#xff1f;4.2 Web标准的构成一…

数模补充(4)灵敏度分析

一、概念 1.1基础概念 灵敏度分析是一种分析模型输出响应程度与模型输入参数变化之间关系的方法&#xff0c;通过对模型输入参数进行变化和分析&#xff0c;来评估模型输出结果的稳定性和可靠性&#xff0c;以及各个输入参数对输出结果的影响程度。 1.2常用模型 1.3基本流程…

springcloud+nacos+gateway案例

一、先搭建好springcloudnacos项目地址:https://javazhong.blog.csdn.net/article/details/128899999二、spring cloud gateway简述Spring Cloud Gateway 是Spring Cloud家族中的一款API网关。Gateway 建立在 Spring Webflux上&#xff0c;目标是提供一个简洁、高效的API网关&a…

FPGA解析串口协议帧4.0版本,移位寄存器实现,提供仿真文件以及源码

这里写目录标题1、前言2、4.0版本优势3、上板验证4、福利&#xff1a;工程代码的获取1、前言 FPGA解析串口协议帧3.0版本参考链接串口协议帧3.0版本 FPGA解析串口协议帧4.0版本&#xff0c;增加了错误重发功能&#xff0c;提供仿真文件以及源码 之前发布了FPGA解析串口协议帧3…

RMPE: Regional Multi-Person Pose Estimation (AlphaPose)阅读笔记

区域多人姿态估计 ICCV 2017 论文链接 代码链接 摘要&#xff1a; 野外多人姿态估计具有挑战性。sota人体检测器不可避免存在定位和识别误差&#xff0c;这些误差可能导致依赖人体检测器的单人姿态估计器&#xff08;SPPE&#xff09;的失败。本文提出了一种新的区域多人姿态估…

二、产品经理——【需求收集】【需求管理】

0. 学习目标 能够理解并描述需求能够收集并管理需求 1. 如何定义需求 1.1. 需求的定义 原始需求&#xff1a;没有经过任何分析&#xff0c;或者没有经过任何额外解读的需求信息 避免日后纠纷&#xff0c;尽量记录一下原始需求&#xff01;先记录下来&#xff0c;后面再进行分…

MySQL数据库(数据库约束)

目录 数据库约束 数据库约束的类型&#xff1a; null约束 &#xff1a; unique约束&#xff08;唯一约束&#xff09;&#xff1a; default约束&#xff08;默认值约束&#xff09;&#xff1a; primary key约束&#xff08;主键约束&#xff09;&#xff1a; for…

Active Directory 04 - Bronze Bit Attack (CVE-2020-17049)

写在最前 如果你是信息安全爱好者&#xff0c;如果你想考一些证书来提升自己的能力&#xff0c;那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里&#xff1a; https://discord.gg/9XvvuFq9Wb我会提供备考过程中尽可能多的帮助&#xff0c;并分享学习和实践过程…

buuctf Basic

buuctf Basic 1.Linux Labs 根据提示我们可以知道需要远程连接linux服务器&#xff0c;这里使用xshell进行如下配置 输入ssh的用户名root&#xff0c;密码123456 连接成功 构造命令 ls …/ 查看文件 查看flag cat …/flag.txt 为flag{8fee8783-1ed5-4b67-90eb-a1d603a0208…

1信号的描述

信号的描述 1.1 连续时间与离散时间信号 一 信号 信号可以描述范围极其广泛的物理现象。信号可以分为确知信号与随机信号&#xff0c;也可以分为连续时间信号与离散时间信号。 确知信号可以表示成一个或几个自变量的函数。 信号的描述 连续时间信号 x(t), x(t1, t2)… 离散时间…

[TPAMI‘21] Heatmap Regression via Randomized Rounding

paper: https://arxiv.org/pdf/2009.00225.pdf code: https://github.com/baoshengyu/H3R 总结&#xff1a;本文提出一套编解码方法&#xff1a; 编码&#xff1a;random-round整数化 激活点响应值表征小数部分&#xff0c;使得GT可以通过编码后的heatmap解码得到&#xff1b…

flowable的变量实操及排坑

主要理解一下知识点 一、名字解释 详情解释传送门 setVariables 和 setVariablesLocal区别? 1.流程变量 在流程执行或者任务执行的过程中&#xff0c;用于设置和获取变量&#xff0c;使用流程变量在流程传递的过程中传递业务参数。 对应的表&#xff1a; act_ru_variable&…

操作系统——4.操作系统的运行机制和体系结构

这篇文章&#xff0c;我们一起来学习一下操作系统的运行机制和体系结构 目录 1.概述 2.运行机制 2.1两种指令 2.2两种处理器状态 ​编辑2.3两种程序 ​编辑2.4 操作系统运行机制小结 3.操作系统内核 ​4.操作系统的体系结构 5.小结 1.概述 首先&#xff0c;我们来看一下…

OpenResty | nginx内部发送请求

文章目录一. http请求的API二. 封装http工具函数三. CJSON工具类四. hash均衡(1) 原理(2) 实现一. http请求的API 关于OpenResty如何安装和编写业务逻辑可以参考这篇文章&#xff1a;点击跳转 nginx提供了内部API用以发送http请求&#xff1a; local resp ngx.location.capt…