使用AOP思想实现项目中对数据权限的处理

AOP简介

问题:AOP的作用是什么?

无入侵式的给代码进行增强

不需要修改代码/方法,就可以对代码/方法进行功能的增强

连接点:项目中的所有 方法

切入点:需要进行功能增强的 方法

通知:功能增强的代码 方法

切面:给哪些方法(切入点) 进行哪些增强(通知)

简单的入门案例

【第一步】导入aop相关坐标

<dependencies>
    <!--spring核心依赖,会将spring-aop传递进来-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

【第二步】定义dao接口与实现类

public interface BookDao {
    public void save();
    public void update();
}

@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }
    public void update(){
        System.out.println("book dao update ...");
    }
}

【第三步】定义通知类,制作通知方法

//通知类必须配置成Spring管理的bean
@Component
public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

【第四步】定义切入点表达式、配置切面(绑定切入点与通知关系)

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,@Pointcut注解要求配置在方法上方
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

【第五步】在配置类中进行Spring注解包扫描和开启AOP功能

@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

【AOP切入点表达式】

execution(* *..*Service+.*(..))

execution(public User com..UserService.findById(..))

execution(public * com.itheima.*.UserService.find*(*))

用切入点表达式太麻烦了 使用自定义注解更为方便  数据权限过滤注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
    /**
     * 部门表的别名
     */
    public String deptAlias() default "";

    /**
     * 用户表的别名
     */
    public String userAlias() default "";
}

用法示例  在接口上方添加注解 这样就可以根据注解找到这些需要鉴权的方法

@Override
    @DataScope(deptAlias = "r", userAlias = "r")
    public List<TbBusiness> selectTbBusinessList(TbBusiness tbBusiness)
    {
        return tbBusinessMapper.selectTbBusinessList(tbBusiness);
    }

设置切入点  通过注解拦截需要数据鉴权的接口方法

// 配置织入点
    @Pointcut("@annotation(com.huike.common.annotation.DataScope)")
    public void dataScopePointCut(){
    }

配置前置通知  由于需要在用户查询数据库前进行权限处理 所以是前置通知

@Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable
    {
        handleDataScope(point);
}

具体处理逻辑  handleDataScope 方法

先获取注解上面的值 用于接下来的权限处理

    /**
     * 是否存在注解,如果存在就获取
     */
    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;
    }
protected void handleDataScope(final JoinPoint joinPoint)
    {
        // 获得注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);
        if (controllerDataScope == null)
        {
            return;
        }
        // 获取当前的用户
        LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNotNull(loginUser))
        {
            SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员,则不过滤数据
            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
            {
            //过滤数据方法
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                        controllerDataScope.userAlias());
        }
    }
}

数据过滤方法

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";

/**
 * 数据权限过滤关键字
 */
public static final String DATA_SCOPE = "dataScope";
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
    {
        System.out.println("过滤数据---------------");
        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 ");
                }
            }
        }

        if (StringUtils.isNotBlank(sqlString.toString()))
        {
            //获取连接点方法的参数  由于传入的值都是继承自BaseEntity 所以可以将其转化成BaseEntity对象 使用其中的params map集合属性封装数据
            Object params = joinPoint.getArgs()[0];
            if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
            {
                BaseEntity baseEntity = (BaseEntity) params;
                baseEntity.getParams().put(DATA_SCOPE, sqlString.substring(4) );
            }
        }
    }

最后会根据不同权限拼接sql 放入BaseEntity的成员属性 

private Map<String, Object> params 中

在执行sql的时候会从sql中取出对应的属性进行sql拼接

 <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
		left join sys_dept d on u.dept_id = d.dept_id
		where u.del_flag = '0'
		<if test="userName != null and userName != ''">
			AND u.user_name like concat('%', #{userName}, '%')
		</if>
		<if test="status != null and status != ''">
			AND u.status = #{status}
		</if>
		<if test="phonenumber != null and phonenumber != ''">
			AND u.phonenumber like concat('%', #{phonenumber}, '%')
		</if>
		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
			AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
		</if>
		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
			AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
		</if>
		<if test="deptId != null and deptId != 0">
			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
		</if>
		<!-- 数据范围过滤 -->
		<!-- 数据范围过滤 -->
		<if test="params.dataScope != null and params.dataScope != ''">
			AND (${params.dataScope})
		</if>
	</select>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值