1.aop的概念详解
-
Pointcut
:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution
方式和annotation
方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。 -
Advice
:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。 -
Aspect
:切面,即Pointcut
和Advice
。 -
Joint point
:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。 -
Weaving
:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。
AOP的体系可以梳理为下图:
2.开始使用aop技术实现数据过滤
数据过滤流程:定义切面和织入点,织入点以注解的形式,注解中定义数据查询表名和数据过滤类别两个参数,在业务查询的service或者controller上添加此注解,并指定参数;然后在切面中实现处理的逻辑,根据表名和过滤类别,编写数据过滤的sql(添加where中的条件实现数据过滤),然后编写的sql以参数的形式写入service或者controller的参数对象中,在mapper中读取sql参数实现数据过滤
概述:通过aop技术,给查询业务的参数对象写入过滤sql,然后在mapper执行时添加此过滤sql实现数据权限过滤。
2.1 定义切面
/**
* 数据过滤处理
*
* @author
*/
@Aspect
@Component
public class DataScopeAspect {
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1";
/**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2";
/**
* 部门数据权限
*/
public static final String DATA_SCOPE_DEPT = "3";
/**
* 部门及以下数据权限
*/
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
/**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5";
/**
* 数据权限过滤关键字(匹配到mapper中)
*/
public static final String DATA_SCOPE = "ywjkDataScope";
// 配置织入点
@Pointcut("@annotation(com.**.**.annotation.DataScope)")
public void dataScopePointCut() {
}
// 处理时机:业务代码执行前
@Before("dataScopePointCut()")
public void doBefore(JoinPoint point) throws Throwable {
handleDataScope(point);
}
// 处理方法
protected void handleDataScope(final JoinPoint joinPoint) {
// 获得注解(可以获取注解上配置的参数,从而实现复杂逻辑)
DataScope controllerDataScope = getAnnotationLog(joinPoint);
if (controllerDataScope == null) {
return;
}
// 获取当前的用户
try {
CurrentUser currentUser = ShiroUtils.getSysUser();
if (currentUser != null) {
// 如果是超级管理员,则不过滤数据
if (!currentUser.isAdmin()) {
dataScopeFilter(joinPoint, currentUser, controllerDataScope.dataAlias(), controllerDataScope.dataMode());
}
}
} catch (Exception e) {
e.printStackTrace();
return;
}
}
/**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param dataAlias 数据查询主表的别名
* @param dataMode 数据过滤类型 0:按照主机过滤,1:按照创建人ID过滤,2:按照创建人账号过滤
*/
public static void dataScopeFilter(JoinPoint joinPoint, CurrentUser user, String dataAlias, String dataMode) {
StringBuilder sqlString = new StringBuilder(); // 实现数据过滤的sql
for (SysRole role : user.getRoles()) {
String dataScope = role.getDataScope();
if (DATA_SCOPE_ALL.equals(dataScope)) {
sqlString = new StringBuilder();
break;
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
if ("0".equals(dataMode)) {
} else if ("1".equals(dataMode)) {
} else if ("2".equals(dataMode)) {
} else if ("3".equals(dataMode)) {
}
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
if ("0".equals(dataMode)) {
} else if ("1".equals(dataMode)) {
} else if ("2".equals(dataMode)) {
} else if ("3".equals(dataMode)) {
}
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
if ("0".equals(dataMode)) {
} else if ("1".equals(dataMode)) {
} else if ("2".equals(dataMode)) {
} else if ("3".equals(dataMode)) {
}
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
if ("0".equals(dataMode)) {
} else if ("1".equals(dataMode)) {
} else if ("2".equals(dataMode)) {
} else if ("3".equals(dataMode)) {
}
}
}
if (StringUtils.isNotBlank(sqlString.toString())) {
BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
/**
* 是否存在注解,如果存在就获取
*/
private DataScope getAnnotationLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(DataScope.class);
}
return null;
}
}
2.2 定义切点
/**
* 数据权限过滤注解
*
* @author
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {
/**
* 数据查询主表的别名(无别名默认不填写)
*/
public String dataAlias() default "";
/**
* 数据过滤方式 0:按照主机过滤,1:按照创建人ID过滤,2:按照创建人账号过滤,3:通知消息特殊处理
*/
public String dataMode() default "0";
}
2.3 需要数据过滤的service或者controller加上注解
@DataScope(dataMode = "1", dataAlias = "ybm")
@Override
public List<YwglBusinessMonitorVo> selectBusinessMonitorList(YwglBusinessMonitor ywglBusinessMonitor) {
return ywglBusinessMonitorMapper.selectBusinessMonitorList(ywglBusinessMonitor);
}
2.4 mapper适配参数
<select id="***" parameterType="**.**"
resultMap="**">
<include refid="**Vo"/>
<where>
<if test="id != null ">and id = #{id}</if>
<!-- 数据范围过滤 -->
${params.DataScope}
</where>
</select>