一起用注解过滤数据吧

背景


工作中肯定用到根据某某机构去过滤数据也就是权限控制
这类数据想当然就应该用 SpringAOP 去实现

思路


首先规定用什么标志让 Spring 识别当前方法需要数据过滤
优雅的使用自定义注解是个不错的选择
注解里面传入当前要进行数据过滤的 Class 对象即可
业务实体类对象里面需要声明一份公共的 OrgIds List 格式用来存放过滤后的查询条件

自定义 DataFilter 注解


package ;

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

import cn.hutool.core.util.ReflectUtil;

/**
 * 数据过滤, 默认过滤组织机构 </br>
 * 可以指定要过滤字段 filterField = { FilterStrategy.orgIds, FilterStrategy.projectIds }
 * 
 * @author 
 * @since 2021-09-19
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataFilter {
	
	/**
	 * 要执行数据过滤的对象
	 * 
	 * @return
	 */
	Class<?> value();
	
	/**
	 * 要执行数据过滤的字段
	 * 
	 * @return
	 */
	FilterStrategy[] filterField() default FilterStrategy.orgIds;
	
	/**
	 * 数据过滤字段策略
	 */
	enum FilterStrategy {
		
		/**
		 * 根据组织机构ID过滤数据
		 */
		orgIds,
		
		/**
		 * 根据项目ID过滤数据
		 */
		projectIds;
	}
	
	/**
	 * 机构查询参数枚举
	 */
	enum OrgQueryParam {
		
		/**
		 * 根据查询对象orgId字段过滤数据
		 */
		orgId,
		
		/**
		 * 根据查询对象did字段过滤数据
		 */
		did,
		
		/**
		 * 根据查询对象createOrgId字段过滤数据
		 */
		createOrgId,

		/**
		 * 根据查询对象orgName字段过滤数据
		 */
		orgName;
		
		/**
		 * 过滤有值字段
		 * 
		 * @param obj
		 * @return
		 */
		public static Object fieldHasValue(Object obj) {
			for (OrgQueryParam queryParam : OrgQueryParam.values()) {
				Object fieldValue = ReflectUtil.getFieldValue(obj, queryParam.name());
				if (fieldValue != null) {
					return fieldValue;
				}
			}
			return null;
		}
		
		/**
		 * 查询条件字段置空
		 * 
		 * @param obj
		 * @return
		 */
		public static void fieldSetNull(Object obj) {
			for (OrgQueryParam queryParam : OrgQueryParam.values()) {
				Object fieldValue = ReflectUtil.getFieldValue(obj, queryParam.name());
				if (fieldValue != null) {
					ReflectUtil.setFieldValue(obj, queryParam.name(), null);
				}
			}
		}
	}
}

数据过滤切面


package ;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

import cn.com.insurance.admin.internal.annotation.DataFilter;
import cn.com.insurance.admin.internal.annotation.DataFilter.FilterStrategy;
import cn.com.insurance.admin.internal.annotation.DataFilter.OrgQueryParam;
import cn.com.insurance.admin.internal.shiro.ShiroContext;
import cn.com.insurance.admin.internal.utils.StringUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 数据过滤切面
 * 
 * @author 
 * @since 2021-09-19
 */
@Slf4j
@Aspect
@Component
public class DataFilterAspect {
	
	/**
	 * 操作切入点,一般情况该方法中再不需要添加其他的代码 使用@Pointcut 来声明切入点表达式 后面的其他通知直接使用方法名直接引用方法名即可
	 */
	@Pointcut("@annotation(cn.com.insurance.admin.internal.annotation.DataFilter)")
	public void operatePointExpression() {
	}
	
	/**
	 * 环绕
	 * 
	 * @param joinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("operatePointExpression()")
	public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
		try {
			Object[] args = joinPoint.getArgs();// 获取参数
			// 超级管理员不进行数据过滤
			boolean isAdmin = ShiroContext.getCurrUser() == null || ShiroContext.getCurrUser().isSuperAdmin();
			if (isAdmin) {
				log.debug("数据过滤执行结果: 当前登录用户是超级管理员: {},过滤参数是: \r\n{}",
						ShiroContext.getCurrUser(),
						JSON.toJSONString(args, true));
			}
			Class<?> classTarget = joinPoint.getTarget().getClass();// 获取目标对象
			Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();// 获取参数类型
			Method objMethod = classTarget.getMethod(joinPoint.getSignature().getName(), par);
			DataFilter annotation = objMethod.getAnnotation(DataFilter.class);
			
			doDataFilter(args, annotation, isAdmin);
			
			log.debug("数据过滤执行结果: 当前登录用户是: {},过滤参数是: \r\n{}", ShiroContext.getCurrUser(), JSON.toJSONString(args, true));
		} catch (Exception e) {
			log.error("数据过滤异常: {}", e);
		}
		return joinPoint.proceed();
	}
	
	/**
	 * 执行数据过滤
	 * 
	 * @param args       方法参数列表
	 * @param annotation 数据过滤注解
	 * @param isAdmin
	 */
	@SuppressWarnings("unchecked")
	private void doDataFilter(Object[] args, DataFilter annotation, boolean isAdmin) {
		Class<? extends Object> clazz = annotation.value();
		for (Object obj : args) {
			// 匹配数据过滤对象
			if (obj.getClass() != clazz) {
				continue;
			}
			// 控制查看机构下级
			Object queryFieldHasValue = OrgQueryParam.fieldHasValue(obj);
			Object hasSubOrg = ReflectUtil.getFieldValue(obj, "hasSubOrg");
			// 1判断前端是否勾选:(含下级选项) 同时判断即使勾选下级但选择组织没有下级也视为未勾选下级
			// 2判断逻辑 非超级管理员
			// 3查询参数有值, 判断当前用户授权机构信息(sys_user_permission)
			// 4以授权机构为主 查询参数为辅
			if (hasSubOrg != null
					&& (boolean) hasSubOrg
						&& queryFieldHasValue != null
						&& ShiroContext.getSubOrgs((String) queryFieldHasValue).size() > 1) {
				ReflectUtil.invoke(obj,
						"set" + StringUtil.upperFirstChar(FilterStrategy.orgIds.name()),
						CollectionUtil.isNotEmpty(ShiroContext.getOrgIds()) && !isAdmin ? ShiroContext
								.getOrgIds() : ShiroContext.getSubOrgs((String) queryFieldHasValue));
				OrgQueryParam.fieldSetNull(obj);
			} else {
				// 5前端未勾选:(含下级选项) 但是查询参数有值
				if (queryFieldHasValue != null && queryFieldHasValue.toString().trim().length() > 0) {
					ReflectUtil.invoke(obj,
							"set" + StringUtil.upperFirstChar(FilterStrategy.orgIds.name()),
							Arrays.asList(queryFieldHasValue));
					OrgQueryParam.fieldSetNull(obj);
				}
			}
			List<String> methods = Arrays.stream(ReflectUtil.getMethodsDirectly(obj.getClass(), true))
					.map(Method::getName)
					.collect(Collectors.toList());
			for (FilterStrategy strategy : annotation.filterField()) {
				String field = strategy.name();
				// 给字段赋值
				String methodName = "set" + StringUtil.upperFirstChar(field);
				if (methods.contains(methodName) && field.contains(FilterStrategy.orgIds.name())) {
					// 机构ID 这里判断上面查询参数没有值的情况给与默认值赋值
					Object fieldValue = ReflectUtil.getFieldValue(obj, field);
					if (!isAdmin // 不是超级管理员且对应字段没有值
							&& (fieldValue == null
									|| (fieldValue instanceof Collection
											&& ((Collection<String>) fieldValue).isEmpty()))) {
						ReflectUtil.invoke(obj, methodName, ShiroContext.getOrgIds());
						OrgQueryParam.fieldSetNull(obj);
					}
				} else if (methods.contains(methodName) && field.contains(FilterStrategy.projectIds.name())) {
					// 项目ID
					ReflectUtil.invoke(obj, methodName, ShiroContext.getProjectIds());
				}
			}
		}
	}
}


公共Vo


package ;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Transient;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

import cn.com.insurance.admin.utils.response.Pagination;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * 公共vo存放分页信息
 * 
 * @author 
 * @since 2021年4月27日14:39:39
 */

@ToString
@Accessors(chain = true)
@EqualsAndHashCode
@JsonIgnoreProperties("pagination")
public class CommonVo implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Transient
	@JsonInclude(JsonInclude.Include.NON_NULL)
	private Integer pageNo, pageSize;
	
	@Transient
	@JSONField(serialize = false)
	private Pagination pagination = new Pagination();
	
	/**
	 * projectIds 权限控制初始化列表查询
	 */
	@Transient
	private List<String> projectIds;
	
	/**
	 * projectIds 权限控制初始化列表查询
	 */
	@Transient
	private List<String> orgIds;
	
	/**
	 * hasSubOrg 权限控制组织机构下级查询
	 */
	@Transient
	private boolean hasSubOrg;
	
	public Integer getPageNo() {
		return pageNo;
	}
	
	public void setPageNo(Integer pageNo) {
		this.pageNo = pageNo;
		this.pagination.setCurrentPage(pageNo);
	}
	
	public Integer getPageSize() {
		return pageSize;
	}
	
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
		this.pagination.setPageSize(pageSize);
	}
	
	public Pagination getPagination() {
		return pagination;
	}
	
	public void setPagination(Pagination pagination) {
		this.pagination = pagination;
	}
	
	public List<String> getProjectIds() {
		return projectIds;
	}
	
	public void setProjectIds(List<String> projectIds) {
		this.projectIds = projectIds;
	}
	
	public List<String> getOrgIds() {
		return orgIds;
	}
	
	public void setOrgIds(List<String> orgIds) {
		this.orgIds = orgIds;
	}
	
	public boolean isHasSubOrg() {
		return hasSubOrg;
	}
	
	public void setHasSubOrg(boolean hasSubOrg) {
		this.hasSubOrg = hasSubOrg;
	}
	
}


公共 Pagination


package ;

import java.io.Serializable;
import java.util.Objects;

/**
 * 分页处理器,非线程安全
 * 
 * @author 
 * @since 2021年5月12日15:53:12
 */
public class Pagination implements Serializable {
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 6787772888002596631L;
	/**
	 * 当前记录索引
	 */
	private int currentIndex = 0;
	/**
	 * 当前页面,默认第一页。注意从1开始计算页码,而不是从0开始
	 */
	private int currentPage = 1;
	/**
	 * 总记录数
	 */
	private int totalRecords = -1;
	/**
	 * 每页显示记录数,默认10条
	 */
	private int pageSize = 10;
	
	/**
	 * 升/降序排列:desc/asc
	 */
	private SortItem sortItem = null;
	
	public Pagination() {
	}
	
	private boolean validate() {
		return this.totalRecords > -1;
	}
	
	public int getCurrentPage() {
		return this.currentPage;
	}
	
	/**
	 * 设置当前页。注意从1开始计算
	 * 
	 * @param currentPage
	 */
	public void setCurrentPage(int currentPage) {
		if (currentPage <= 0) {
			throw new IllegalArgumentException("the parameter 'currentPage' can not be zero or negative.");
		}
		this.currentPage = currentPage;
		if (validate()) {
			prepare();
		}
	}
	
	public int getCurrentIndex() {
		if (validate()) {
			prepare();
		}
		return this.currentIndex;
	}
	
	public int getTotalRecodes() {
		return this.totalRecords;
	}
	
	public void setTotalRecodes(int totalRecords) {
		if (totalRecords < 0) {
			totalRecords = 10;
//			throw new IllegalArgumentException(
//					"the parameter 'totalRecords' can not be negative:" + "[ totalRecords = " + totalRecords + " ]");
		}
		this.totalRecords = totalRecords;
		prepare();
	}
	
	public int getPageSize() {
		return this.pageSize;
	}
	
	public void setPageSize(int pageSize) {
		if (pageSize <= 0) {
			throw new IllegalArgumentException("the parameter 'pageSize' can not be zero or negative.");
		}
		this.pageSize = pageSize;
	}
	
	private void prepare() {
		if (this.totalRecords < 0) {
			totalRecords = 10;
//			throw new IllegalStateException(
//					"the field \"" + Pagination.class.getName() + ".totalRecords\" has not initialized.");
		}
		if (this.currentPage == 0) {
			this.currentPage = 1;
		}
		if (this.pageSize == 0) {
			this.pageSize = 10;
		}
		if (this.currentPage > this.getTotalPage()) {
			this.currentPage = this.getTotalPage();
		}
		this.currentIndex = ((this.currentPage - 1) * this.pageSize);
		if (this.currentIndex > this.totalRecords) {
			this.currentIndex = (this.totalRecords - (this.currentIndex - this.totalRecords));
		}
	}
	
	public int getTotalPage() {
		if (this.totalRecords <= this.pageSize) {
			return 1;
		}
		
		int m = this.totalRecords % this.pageSize;
		int totalPage = (this.totalRecords - m) / this.pageSize;
		if (m > 0) {
			totalPage++;
		}
		return totalPage;
	}
	
	/**
	 *
	 * @return
	 */
	public boolean isInitialized() {
		return this.validate();
	}
	
	/**
	 * do the same thing as this.setTotalRecodes(int totalRecords)
	 * 
	 * @param totalRecords
	 */
	public void init(int totalRecords) {
		this.setTotalRecodes(totalRecords);
	}
	
	public SortItem getSortItem() {
		return sortItem;
	}
	
	public void setSortItem(SortItem sortItem) {
		this.sortItem = sortItem;
	}
	
	/**
	 * 判断是否有下一页
	 * 
	 * @return
	 */
	public boolean hasNext() {
		int nextPage = this.currentPage + 1;
		int totalPage = getTotalPage();
		
		return nextPage <= totalPage;
	}
	
	/**
	 * 往后翻一页
	 */
	public void next() {
		if (hasNext()) {
			this.currentPage += 1;
		}
	}
	
	@Override
	public String toString() {
		return "Pagination{"
				+ "currentIndex="
					+ currentIndex
					+ ", currentPage="
					+ currentPage
					+ ", totalRecords="
					+ totalRecords
					+ ", pageSize="
					+ pageSize
					+ ", sortItem="
					+ sortItem
					+ '}';
	}
	
	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		Pagination that = (Pagination) o;
		return currentIndex == that.currentIndex
				&& currentPage == that.currentPage
					&& totalRecords == that.totalRecords
					&& pageSize == that.pageSize
					&& Objects.equals(sortItem, that.sortItem);
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(currentIndex, currentPage, totalRecords, pageSize, sortItem);
	}
}

如何使用


	@DataFilter(CoreAccountingBill.class)
	@Override
	public List<CoreAccountingBill> queryCoreAccountingBillList(CoreAccountingBill coreAccountingBill, Pagination pagination) {
		PageInfo<CoreAccountingBill> pageInfo = PageHelper
				.startPage(pagination.getCurrentPage(), pagination.getPageSize())
				.doSelectPageInfo(() -> coreAccountingBillMapper.queryCoreAccountingBillList(coreAccountingBill));
		pagination.setTotalRecodes((int) pageInfo.getTotal());
	
		return pageInfo.getList();
	}
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值