目录
1.什么是AOP
简介
2.若依的AOP实现
2.1若依数据过滤AOP
定义注解
实现切面类
2.2数据源AOP
1.什么是AOP
简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,是Spring的三大核心思想之一。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
换句话说,浅显的理解就是在项目中横切一刀,放置一个过滤网来拦截数据或实现代码复用。
原先的项目
文章图片
实现代码复用,减少代码量,但是需要不断引入和配置。
文章图片
实现AOP,切入过程中,既实现了代码复用,也将模块完成独立出来,减少了工作量
文章图片
详情参看大佬文章【SpringBoot-3】切面AOP实现权限校验:实例演示与注解全解_云深i不知处的博客-CSDN博客
2.若依的AOP实现 简单介绍一下若依的AOP使用
引入依赖
org.springframework.boot
spring-boot-starter-aop
2.1若依数据过滤AOP 定义注解
/**
* 数据权限过滤注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
}
@DataScope,用于数据过滤的注解,有两个参数默认为“ ”,这两个参数可以用于不同表的数据过滤,可以根据自己需求添加多个参数
有三个注解实现配置
@Target用于描述注解的使用范围,被描述的注解可以用在什么地方,参数
文章图片
@Retention 用于定义注解的生命周期,参数
SOURCE | 在源文件中有效(即源文件保留) |
---|---|
CLASS | 在class文件中有效(即class保留) |
RUNTIME | 在运行时有效(即运行时保留) |
实现切面类
一步一步来看
先定义切面类
**
* 数据过滤处理
*
* @author ruoyi
*/
@Aspect
@Component
public class DataScopeAspect
@Aspect 用于表示该类为切面类@Component 组件加入容器中
@Before("@annotation(controllerDataScope)")
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
{
clearDataScope(point);
handleDataScope(point, controllerDataScope);
}
@Before用于定义方法,该方法在切面切入目标方法之前执行
参数 JoinPoint:该对象封装了SpringAOP中切面方法的信息,可以获取到封装了该方法信息的JoinPoint对象。
参数 DataScope 注解对象,包含注解填入的两个参数
该方法先调用了clearDataScope()方法
/**
* 拼接权限sql前先清空params.dataScope参数防止注入
*/
private void clearDataScope(final JoinPoint joinPoint)
{
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
{
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, "");
}
}
作用就是清空params.DATA_SCOPE参数防止注入,DATA_SCOPE是请求经过AOP切面后被放入的参数,用于在sql语句中的数据过滤,所以需要清空,以免sql注入,虽然可能性不大
【若依ruoyiAOP切面用于数据过滤和权限处理实例】后调用handleDataScope()方法
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
{
// 获取当前的用户
SysUser currentUser = ShiroUtils.getSysUser();
if (currentUser != null)
{
// 如果是超级管理员,则不过滤数据
if (!currentUser.isAdmin())
{
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias());
}
}
可以看出,如果请求中包含用户信息而且不为管理员才可以进行数据的过滤
方法dataScopeFilter()才是逻辑的主要部分
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
{
StringBuilder sqlString = new StringBuilder();
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))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
if (StringUtils.isNotBlank(userAlias))
{
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
}
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
}
}
}
逻辑很简单,用请求中的dataScope值(数据库查询出的值)来判断该用户拥有哪种权限
然后根据@DataScope注解的值,来判断这个用户正在访问哪张表,此时就可以将一段字符串放入sqlString中
if (StringUtils.isNotBlank(sqlString.toString()))
{
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
{
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
如果sqlString不为空,且被注解定义的方法的参数继承了BaseEntity(Entity基类
)就将sqlString放入参数中
最后,将@DataScope注解放在Servcie层的方法上
@DataScope(deptAlias = "aaa")
public List selectList(Params param)
{
return Mapper.selectList(param);
}
在Mapper中添加
${params.dataScope}
动态sql注入
2.2数据源AOP
推荐阅读
- Spring|若依-数据权限管理-DataScope
- 若依框架|基于若依框架的数据权限。
- spring|雷神《谷粒商城》spring-cloud-alibaba(阿里巴巴网关)gateway3.0.4,配置问题
- springBoot|SpringBoot整合Spring Security+JWT实现前后端分离登录权限处理
- 过滤器|SpringBoot官宣(最低要求 Java 17!怎么办())
- SSM框架|SpringMVC全局乱码过滤器、注解@RequestParam、自定义类型转换器
- #|Spring|SpringMVC的请求和响应以及文件上传
- SSM框架|SpringMVC回写数据方式方法
- SSM框架|SpringMVC获得请求数据类型