使用自定义注解实现数据权限

/**
 * 自定义注解,作用于数据权限
 *
 * @author LZJ
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserDataPermission {

}


/**
 * 拦截器
 *
 * @author LZJ
 */
@Data
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {

    private static final String[] KEYWORDS = {
            "com.performance.mapper.passable",
            "selectById"
    };

    /**
     * 数据权限处理器
     */
    private DataPermissionHandler dataPermissionHandler;

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
            return;
        }
        String mapperShow = ms.getId();
        //不判断权限的情况
        if (shouldSkip(mapperShow)) {
            return;
        }
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        mpBs.sql(this.parserSingle(mpBs.sql(), ms.getId()));
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        SelectBody selectBody = select.getSelectBody();
        if (selectBody instanceof PlainSelect) {
            this.setWhere((PlainSelect) selectBody, (String) obj);
        } else if (selectBody instanceof SetOperationList) {
            SetOperationList setOperationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodyList = setOperationList.getSelects();
            selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
        }
    }

    /**
     * 设置 where 条件
     *
     * @param plainSelect  查询对象
     * @param whereSegment 查询条件片段
     */
    private void setWhere(PlainSelect plainSelect, String whereSegment) {

        Expression sqlSegment = this.dataPermissionHandler.getSqlSegment(plainSelect, whereSegment);
        if (null != sqlSegment) {
            plainSelect.setWhere(sqlSegment);
        }
    }

    /**
     * 不判断权限的情况
     */
    private boolean shouldSkip(String mapperShow) {
        for (String keyword : KEYWORDS) {
            if (mapperShow.contains(keyword)) {
                return true;
            }
        }
        return false;
    }
}



/**
 * 数据权限拦截处理器
 *
 * @author LZJ
 */
@Slf4j
public class DataPermissionHandler {

    /**
     * 获取数据权限 SQL 片段
     *
     * @param plainSelect  查询对象
     * @param whereSegment 查询条件片段
     * @return JSqlParser 条件表达式
     */
    @SneakyThrows(Exception.class)
    public Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) {

        log.debug("开始进行权限过滤mappedStatementId: {}", whereSegment);
        //有权
        String pass = " 1 = 1 ";
        //无权
        String refuse = " 1 = 2 ";
        // 待执行 SQL Where 条件表达式
        Expression where = plainSelect.getWhere();
        if (where == null) {
            where = new HexValue(pass);
        }
        log.debug("查询语句: {},where条件: {}", plainSelect, where);
        Table fromItem = (Table) plainSelect.getFromItem();
        // 有别名用别名,无别名用表名,防止字段冲突报错
        Alias fromItemAlias = fromItem.getAlias();
        String mainTableName = fromItemAlias == null ? fromItem.getName() : fromItemAlias.getName();
        //获取mapper名称
        String className = whereSegment.substring(0, whereSegment.lastIndexOf("."));
        //获取当前mapper 的方法
        Method[] methods = Class.forName(className).getMethods();
        //获取方法名
        String methodName = whereSegment.substring(whereSegment.lastIndexOf(".") + 1);
        //获取token信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = request.getHeader(RequestHeaderName.TOKEN);
        RedisService redisService = SpringUtil.getBean(RedisService.class);
        LoginToken loginToken = redisService.getCacheObject(TokenConstants.REDIS_KEY_PREFIX + token);
        //回调的时候会是NULL
        if (loginToken == null) {
            return where;
        }
        //管理员看全部
        if (loginToken.getSuperManager() == 1) {
            return where;
        }
        RoleDataAndDeptBo roleDataPermissions = loginToken.getRoleDataPermissions();
        if (roleDataPermissions == null
                || CollUtil.isEmpty(roleDataPermissions.getDataScope())) {
            //无权查看
            return new HexValue(refuse);
        }
        //1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限
        Set<String> dataScope = roleDataPermissions.getDataScope();
        Set<String> deptIdSet = roleDataPermissions.getDeptIdSet();
        //全部数据权限
        if (dataScope.contains(DataScopeType.ALL.getCode())) {
            return where;
        }
        //变态需求,需要根据查询项不走数据权限
        if ("xxx".equals(mainTableName)) {
            String string = where.toString();
            log.debug("where格式化: {}", string);
            if (string.contains("AND 3 =")) {
                return where;
            }
        }
        //不包含本人数据权限
        if (!dataScope.contains(DataScopeType.SELF.getCode())) {
            if (CollUtil.isEmpty(deptIdSet)) {
                //无权查看
                where = new HexValue(refuse);
                return where;
            }
            //遍历判断mapper 的所有方法,判断方法上是否有 UserDataPermission
            for (Method m : methods) {
                if (!Objects.equals(m.getName(), methodName)) {
                    continue;
                }
                UserDataPermission annotation = m.getAnnotation(UserDataPermission.class);
                if (annotation == null) {
                    return where;
                }
                //部门权限
                ItemsList deptList = new ExpressionList(deptIdSet.stream().map(StringValue::new).collect(Collectors.toList()));
                InExpression inExpressiondept = new InExpression(new Column(mainTableName + ".department_id"), deptList);
                return new AndExpression(where, inExpressiondept);
            }
        } else {
            //包含用户权限,不限部门权限
            //是否只包含用户权限
            boolean flag = CollUtil.isEmpty(deptIdSet);
            //遍历判断mapper 的所有方法,判断方法上是否有 UserDataPermission
            for (Method m : methods) {
                if (!Objects.equals(m.getName(), methodName)) {
                    continue;
                }
                UserDataPermission annotation = m.getAnnotation(UserDataPermission.class);
                if (annotation == null) {
                    return where;
                }
                //用户权限
                EqualsTo usesEqualsTo = new EqualsTo();
                usesEqualsTo.setLeftExpression(new Column(mainTableName + ".creator_id"));
                usesEqualsTo.setRightExpression(new StringValue(loginToken.getUserId()));
                if (flag) {
                    return new AndExpression(where, usesEqualsTo);
                }
                //部门权限
                ItemsList deptList = new ExpressionList(deptIdSet.stream().map(StringValue::new).collect(Collectors.toList()));
                InExpression inExpressiondept = new InExpression(new Column(mainTableName + ".department_id"), deptList);
                //部门权限or用户权限
                OrExpression orExpression = new OrExpression();
                orExpression.setLeftExpression(inExpressiondept);
                orExpression.setRightExpression(usesEqualsTo);
                //救命的sql拼接 https://www.cnblogs.com/hanzhe/articles/17037141.html
                Parenthesis parenthesis = new Parenthesis(orExpression);
                return new AndExpression(where, parenthesis);
            }
        }
        //说明无权查看,
        where = new HexValue(refuse);
        return where;
    }
}




@Configuration
public class MybatisPlusInterceptorConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {

        //如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //数据权限处理
        interceptor.addInnerInterceptor(dataPermissionInterceptor());
        //配置多租户
        interceptor.addInnerInterceptor(tenantLineInnerInterceptor());
        //分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        //配置乐观锁
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        return interceptor;
    }

    /**
     * 分页插件,自动识别数据库类型
     */
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        // 分页合理化
        paginationInnerInterceptor.setOverflow(true);
        paginationInnerInterceptor.setOptimizeJoin(true);
        paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL);
        return paginationInnerInterceptor;
    }

    /**
     * 乐观锁插件
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

    /**
     * 多租户插件
     */
    public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
        return new TenantLineInnerInterceptor(new MultiTenantHandler());
    }

    /**
     * 数据权限
     */
    public DataPermissionInterceptor dataPermissionInterceptor() {
        return new DataPermissionInterceptor(new DataPermissionHandler());
    }

}


/**
 * 自定义 Mapper 接口, 实现 自定义扩展
 *
 * @param <T> table 泛型
 * @author MUHUA
 */
@SuppressWarnings("unchecked")
public interface DataPermissionMapper<T> extends BaseMapper<T> {

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    @Override
    @UserDataPermission
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    @Override
    @UserDataPermission
    <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值