人人框架 数据权限的使用

前言:本系统采⽤注解的⽅式,实现了数据权限的功能,在需要数据权限的service⽅法上,添加@DataFilter 注解,就可以达到数据过滤的功能,也就是我们常说的数据权限。该实现⽅式,适应绝⼤多数企业后台管理系统,对数据权限的要求。


第一步、定义注解

/**
 * 数据过滤注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataFilter {
    /**
     * 表的别名
     */
    String tableAlias() default "";

    /**
     * 用户ID
     */
    String userId() default "creator";

    /**
     * 部门ID
     */
    String deptId() default "dept_id";

}

第二步、配置数据过滤的切面

上⾯定义好了注解,我们再来看下具体的实现,也就是 @DataFilter 注解的切⾯实现类 DataFilterAspect ,这里定义了⼀个切⼊点,只要⽅法上加了 @DataFilter 注解,执⾏该⽅法之前,会进⼊ dataFilter() ⽅法

数据权限是通过dept_id、user_id进⾏数据过滤的,所以表⾥需要有这2 个字段,不然数据权
限就⽆法实现,当然这 2 个字段名是可以修改的,下⾯就是把user_id修改成了sale_id
@DataFilter(userId = "sale_id")
public PageData<SalesReportDTO> page(Map<String, Object> params) {
        IPage<SalesReportEntity> page = baseDao.selectPage(
                getPage(params, Constant.CREATE_DATE, false),
                getWrapper(params)
        );
        return getPageData(page, SalesReportDTO.class);
}
@Aspect
@Component
public class DataFilterAspect {

    @Pointcut("@annotation(io.renren.common.annotation.DataFilter)")
    public void dataFilterCut() {

    }

    @Before("dataFilterCut()")
    public void dataFilter(JoinPoint point) {
        //获取方法中的第一个参数
        Object params = point.getArgs()[0];
        if (params != null && params instanceof Map) {
            UserDetail user = SecurityUser.getUser();

            //如果是超级管理员,则不进行数据过滤
            if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
                return;
            }

            try {
                //否则进行数据过滤
                Map map = (Map) params;
                String sqlFilter = getSqlFilter(user, point);
                map.put(Constant.SQL_FILTER, new DataScope(sqlFilter));
            } catch (Exception e) {

            }

            return;
        }

        throw new RenException(ErrorCode.DATA_SCOPE_PARAMS_ERROR);
    }

    /**
     * 获取数据过滤的SQL
     */
    private String getSqlFilter(UserDetail user, JoinPoint point) throws Exception {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = point.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
        DataFilter dataFilter = method.getAnnotation(DataFilter.class);

        //获取表的别名
        String tableAlias = dataFilter.tableAlias();
        if (StrUtil.isNotBlank(tableAlias)) {
            tableAlias += ".";
        }

        StringBuilder sqlFilter = new StringBuilder();
        sqlFilter.append(" (");

        //部门ID列表
        List<Long> deptIdList = user.getDeptIdList();
        if (CollUtil.isNotEmpty(deptIdList)) {
            sqlFilter.append(tableAlias).append(dataFilter.deptId());

            sqlFilter.append(" in(").append(StringUtils.join(deptIdList, ",")).append(")");
        }

        //查询本人数据
        if (CollUtil.isNotEmpty(deptIdList)) {
            sqlFilter.append(" or ");
        }
        sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId());

        sqlFilter.append(")");

        return sqlFilter.toString();
    }
}

因为使⽤了Spring AOP的 @Before ,Spring AOP将按照如下图的顺序执⾏

需要说明的是这里的部门是用户角色对应的部门,专门用来做数据权限的,上⾯的代码中,可以看到有这么⼀⾏ map.put(Constant.SQL_FILTER, new DataScope(sqlFilter)) ,这⾏的意思,就是把过滤的SQL条件,追加到map参数⾥,map参数对应的key为 Constant.SQL_FILTER,通过以上操作我们的数据过滤条件就完成了


 第三步、使⽤mybatis-plus进⾏数据过滤

@DataFilter(userId = "sale_id")
public PageData<SalesReportDTO> page(Map<String, Object> params) {
        IPage<SalesReportEntity> page = baseDao.selectPage(
                getPage(params, Constant.CREATE_DATE, false),
                getWrapper(params)
        );
        return getPageData(page, SalesReportDTO.class);
}
public QueryWrapper<SalesReportEntity> getWrapper(Map<String, Object> params){
     QueryWrapper<SalesReportEntity> wrapper = new QueryWrapper<>();
     //数据过滤
    if (params.get(Constant.SQL_FILTER) != null){
        wrapper.apply(
                params.get(Constant.SQL_FILTER) != null,
                params.get(Constant.SQL_FILTER).toString()
        );
    }
     return wrapper;
}

上⾯的代码,先判断Map⾥的 Constant.SQL_FILTER 是否为null,不为空则追加进去,这样就可以实现数据过滤了

接着会走数据权限的过滤器,如果是自定义写的sql则会进入下面的逻辑,进行拼接新SQL

public class DataFilterInterceptor implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
                            RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        DataScope scope = getDataScope(parameter);
        // 不进行数据过滤
        if(scope == null || StrUtil.isBlank(scope.getSqlFilter())){
            return;
        }

        // 拼接新SQL
        String buildSql = getSelect(boundSql.getSql(), scope);

        // 重写SQL
        PluginUtils.mpBoundSql(boundSql).sql(buildSql);
    }

    private DataScope getDataScope(Object parameter){
        if (parameter == null){
            return null;
        }

        // 判断参数里是否有DataScope对象
        if (parameter instanceof Map) {
            Map<?, ?> parameterMap = (Map<?, ?>) parameter;
            for (Map.Entry entry : parameterMap.entrySet()) {
                if (entry.getValue() != null && entry.getValue() instanceof DataScope) {
                    return (DataScope) entry.getValue();
                }
            }
        } else if (parameter instanceof DataScope) {
            return (DataScope) parameter;
        }

        return null;
    }

    private String getSelect(String buildSql, DataScope scope){
        try {
            Select select = (Select) CCJSqlParserUtil.parse(buildSql);
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();

            Expression expression = plainSelect.getWhere();
            if(expression == null){
                plainSelect.setWhere(new StringValue(scope.getSqlFilter()));
            }else{
                AndExpression andExpression =  new AndExpression(expression, new StringValue(scope.getSqlFilter()));
                plainSelect.setWhere(andExpression);
            }

            return select.toString().replaceAll("'", "");
        }catch (JSQLParserException e){
            return buildSql;
        }
    }
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值