Springboot管理系统数据权限过滤(二)——SQL拦截器

上一节Springboot管理系统数据权限过滤——ruoyi实现方案对数据权限实现方案有了认识,本文将进一步优化权限过滤方案,实现对业务代码零入侵。

回顾上一章中权限方案:

  • 主要是通过注解拦截,拼接好权限脚本后,放到对象变量里面,然后在SQL中拼接该变量;使业务代码被入侵了。

为了实现对业务零入侵,实则是在SQL编写的时候,希望通过框架实现权限脚本的自动拼接,而非人为添加。
本文权限控制需要达到的效果:

  • 1.还是对组织进行权限控制;
  • 2.去掉编写sql时拼接权限过滤参数;使权限代码0侵入;

步骤:

1. 搭建springboot框架,完成mybatisplus集成和swagger集成

pom.xml文件


<parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.3.RELEASEversion>
        <relativePath/>
    parent>

<dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency> 
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.48version>
        dependency>

        
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.1version>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.3.2version>
        dependency>

        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>2.9.2version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>2.9.2version>
        dependency>
    dependencies>

application.yaml

spring:
  # datasource 数据源配置内容
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456

# mybatis-plus 配置内容
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
  global-config:
    db-config:
      id-type: auto # ID 主键自增
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.luo.chengrui.labs.lab02.dataobject # 配置数据库实体包路径

# logging
logging:
  level:
    # dao 开启 debug 模式 mybatis 输入 sql
    com:
      luo:
        chengrui:
          labs: debug

UserDao.java

package com.luo.chengrui.labs.lab02.dataobject;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.util.Date;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/20
 */
@Data
@Accessors(chain = true)
@TableName(value = "users")
public class UserDO {

    /**
     * 用户编号
     */
    private Long id;
    /**
     * 账号
     */
    private String username;
    /**
     * 密码(明文)
     * 

* ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; }

UserMapper.java

package com.luo.chengrui.labs.lab02.mapper;

import com.luo.chengrui.labs.lab02.dataobject.UserDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;


@Mapper
public interface UserMapper {

    UserDO selectById(@Param("id") Integer id);

    List<UserDO> selectList();
}

UserMapper.xml


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luo.chengrui.labs.lab02.mapper.UserMapper">

    <sql id="FIELDS">
        id
        , username
    sql>

    <select id="selectById" parameterType="Integer" resultType="UserDO">
        SELECT
        <include refid="FIELDS"/>
        FROM users
        WHERE id = #{id}
    select>

    <select id="selectList" resultType="UserDo">
        SELECT
        <include refid="FIELDS"/>
        FROM users
    select>

mapper>

UserService.java

package com.luo.chengrui.labs.lab02.service;

import com.luo.chengrui.labs.lab02.annotation.DataScope;
import com.luo.chengrui.labs.lab02.dataobject.UserDO;
import com.luo.chengrui.labs.lab02.mapper.UserMapper;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/21
 */
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    private UserService self() {
        return (UserService) AopContext.currentProxy();
    }

    /**
     * 方法未使用 @Transactional 注解,不会开启事务。
     * 对于 OrderMapper 和 UserMapper 的查询操作,分别使用其接口上的 @DS 注解,找到对应的数据源,执行操作。
     * 这样一看,在未开启事务的情况下,我们已经能够自由的使用多数据源落。
     */
    public void method() {
        // 查询订单
        UserDO user = userMapper.selectById(1);
        System.out.println(user);
    }

    @DataScope
    public void method01() {
        // 查询订单
        UserDO user = userMapper.selectById(1);
        System.out.println(user);
    }

    @DataScope
    public List<UserDO> selectList() {
        return userMapper.selectList();
    }
}


UserController.java

package com.luo.chengrui.labs.lab02.controller;

import com.luo.chengrui.labs.lab02.dataobject.UserDO;
import com.luo.chengrui.labs.lab02.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/17
 */
@RestController
@RequestMapping("/users")
@Api(tags = "用户 API 接口")
public class UserController {

    @Autowired
    UserService userService;

    @GetMapping("/list")
    @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表")
    public List<UserDO> list() {
        // 查询列表
        List<UserDO> result = userService.selectList();
        // 返回列表
        return result;
    }

}

SwaggerConfiguration.java

package com.luo.chengrui.labs.lab02.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * 访问地址:/swagger-ui.html
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/17
 */
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
    @Bean
    public Docket createRestApi() {
        // 创建 Docket 对象
        return new Docket(DocumentationType.SWAGGER_2) // 文档类型,使用 Swagger2
                .apiInfo(this.apiInfo()) // 设置 API 信息
                // 扫描 Controller 包路径,获得 API 接口
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.luo.chengrui.labs.lab02.controller"))
                .paths(PathSelectors.any())
                // 构建出 Docket 对象
                .build();
    }

    /**
     * 创建 API 信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("测试接口文档示例")
                .description("我是一段描述")
                .version("1.0.0") // 版本号
                .contact(new Contact("芋艿", "http://www.iocoder.cn", "[email protected]")) // 联系人
                .build();
    }
}

Lab0201Application.java

package com.luo.chengrui.labs.lab02;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/21
 */
@SpringBootApplication
@MapperScan(basePackages = "com.luo.chengrui.labs.lab02.mapper")
public class Lab0201Application {

    public static void main(String[] args) {
        SpringApplication.run(Lab0201Application.class, args);
    }
}

到此完成框架搭建,访问:http://localhost:8080/swagger-ui.html,可看到以下页面即为成功。

Springboot管理系统数据权限过滤(二)——SQL拦截器_第1张图片

2. 配置sql拦截器

创建 DataPermissionDatabaseInterceptor.java 类,类中大部分代码是对sql的解析,对表名的解析,对where语句的解析,真正需要关注的逻辑只是少部分,本部分代码里暂未添加对权限的控制,在下一文章中添加。
该类继承JsqlParserSupport ,同时实现InnerInterceptor接口

  • JsqlParserSupport 用于解析sql语句,可以对sql进行改造;方法有:processSelect、processUpdate、processDelete等;
  • InnerInterceptor 在执行sql语句之前的拦截器,在JspParserSuport方法执行之前执行 。方法有:beforeQuery、beforeUpdate、beforePrepare等。
    通过实现以上5个方法即可对sql进行改造。
package com.luo.chengrui.labs.lab02.datapermission;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 数据权限拦截器,通过 {@link } 数据权限规则,重写 SQL 的方式来实现
 * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, List)} 方法
 * 主要是在执行SQL前拦截器,在执行之前可重写SQL
 *
 * @author 芋道源码
 */
@RequiredArgsConstructor
public class DataPermissionDatabaseInterceptor extends JsqlParserSupport implements InnerInterceptor {

    private static final String MYSQL_ESCAPE_CHARACTER = "`";

    @Override // SELECT 场景
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      

        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        try {
            // 初始化上下文
            // 处理 SQL
            mpBs.sql(parserSingle(mpBs.sql(), null));
        } finally {
        }
    }

    @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景(因为 INSERT 不需要数据权限)
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            // 获得 Mapper 对应的数据权限的规则
          

            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            try {
                // 初始化上下文
                // 处理 SQL
                mpBs.sql(parserMulti(mpBs.sql(), null));
            } finally {
            }
        }
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        processSelectBody(select.getSelectBody());
        List<WithItem> withItemsList = select.getWithItemsList();
        if (!CollectionUtils.isEmpty(withItemsList)) {
            withItemsList.forEach(this::processSelectBody);
        }
    }

    /**
     * update 语句处理
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        final Table table = update.getTable();
        update.setWhere(this.builderExpression(update.getWhere(), table));
    }

    /**
     * delete 语句处理
     */
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        delete.setWhere(this.builderExpression(delete.getWhere(), delete.getTable()));
    }

    // ========== 和 TenantLineInnerInterceptor 一致的逻辑 ==========

    protected void processSelectBody(SelectBody selectBody) {
        if (selectBody == null) {
            return;
        }
        if (selectBody instanceof PlainSelect) {
            processPlainSelect((PlainSelect) selectBody);
        } else if (selectBody instanceof WithItem) {
            WithItem withItem = (WithItem) selectBody;
            processSelectBody(withItem.getSubSelect().getSelectBody());
        } else {
            SetOperationList operationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodyList = operationList.getSelects();
            if (CollectionUtils.isNotEmpty(selectBodyList)) {
                selectBodyList.forEach(this::processSelectBody);
            }
        }
    }

    /**
     * 处理 PlainSelect
     */
    protected void processPlainSelect(PlainSelect plainSelect) {
        //#3087 github
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        if (CollectionUtils.isNotEmpty(selectItems)) {
            selectItems.forEach(this::processSelectItem);
        }

        // 处理 where 中的子查询
        Expression where = plainSelect.getWhere();
        processWhereSubSelect(where);

        // 处理 fromItem
        FromItem fromItem = plainSelect.getFromItem();
        List<Table> list = processFromItem(fromItem);
        List<Table> mainTables = new ArrayList<>(list);

        // 处理 join
        List<Join> joins = plainSelect.getJoins();
        if (CollectionUtils.isNotEmpty(joins)) {
            mainTables = processJoins(mainTables, joins);
        }

        // 当有 mainTable 时,进行 where 条件追加
        if (CollectionUtils.isNotEmpty(mainTables)) {
            plainSelect.setWhere(builderExpression(where, mainTables));
        }
    }

    private List<Table> processFromItem(FromItem fromItem) {
        // 处理括号括起来的表达式
        while (fromItem instanceof ParenthesisFromItem) {
            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
        }

        List<Table> mainTables = new ArrayList<>();
        // 无 join 时的处理逻辑
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            mainTables.add(fromTable);
        } else if (fromItem instanceof SubJoin) {
            // SubJoin 类型则还需要添加上 where 条件
            List<Table> tables = processSubJoin((SubJoin) fromItem);
            mainTables.addAll(tables);
        } else {
            // 处理下 fromItem
            processOtherFromItem(fromItem);
        }
        return mainTables;
    }

    /**
     * 处理where条件内的子查询
     * 

* 支持如下: * 1. in * 2. = * 3. > * 4. < * 5. >= * 6. <= * 7. <> * 8. EXISTS * 9. NOT EXISTS *

* 前提条件: * 1. 子查询必须放在小括号中 * 2. 子查询一般放在比较操作符的右边 * * @param where where 条件 */ protected void processWhereSubSelect(Expression where) { if (where == null) { return; } if (where instanceof FromItem) { processOtherFromItem((FromItem) where); return; } if (where.toString().indexOf("SELECT") > 0) { // 有子查询 if (where instanceof BinaryExpression) { // 比较符号 , and , or , 等等 BinaryExpression expression = (BinaryExpression) where; processWhereSubSelect(expression.getLeftExpression()); processWhereSubSelect(expression.getRightExpression()); } else if (where instanceof InExpression) { // in InExpression expression = (InExpression) where; Expression inExpression = expression.getRightExpression(); if (inExpression instanceof SubSelect) { processSelectBody(((SubSelect) inExpression).getSelectBody()); } } else if (where instanceof ExistsExpression) { // exists ExistsExpression expression = (ExistsExpression) where; processWhereSubSelect(expression.getRightExpression()); } else if (where instanceof NotExpression) { // not exists NotExpression expression = (NotExpression) where; processWhereSubSelect(expression.getExpression()); } else if (where instanceof Parenthesis) { Parenthesis expression = (Parenthesis) where; processWhereSubSelect(expression.getExpression()); } } } protected void processSelectItem(SelectItem selectItem) { if (selectItem instanceof SelectExpressionItem) { SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; if (selectExpressionItem.getExpression() instanceof SubSelect) { processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody()); } else if (selectExpressionItem.getExpression() instanceof Function) { processFunction((Function) selectExpressionItem.getExpression()); } } } /** * 处理函数 *

支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)

*

fixed gitee pulls/141

* * @param function */
protected void processFunction(Function function) { ExpressionList parameters = function.getParameters(); if (parameters != null) { parameters.getExpressions().forEach(expression -> { if (expression instanceof SubSelect) { processSelectBody(((SubSelect) expression).getSelectBody()); } else if (expression instanceof Function) { processFunction((Function) expression); } }); } } /** * 处理子查询等 */ protected void processOtherFromItem(FromItem fromItem) { // 去除括号 while (fromItem instanceof ParenthesisFromItem) { fromItem = ((ParenthesisFromItem) fromItem).getFromItem(); } if (fromItem instanceof SubSelect) { SubSelect subSelect = (SubSelect) fromItem; if (subSelect.getSelectBody() != null) { processSelectBody(subSelect.getSelectBody()); } } else if (fromItem instanceof ValuesList) { logger.debug("Perform a subQuery, if you do not give us feedback"); } else if (fromItem instanceof LateralSubSelect) { LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem; if (lateralSubSelect.getSubSelect() != null) { SubSelect subSelect = lateralSubSelect.getSubSelect(); if (subSelect.getSelectBody() != null) { processSelectBody(subSelect.getSelectBody()); } } } } /** * 处理 sub join * * @param subJoin subJoin * @return Table subJoin 中的主表 */ private List<Table> processSubJoin(SubJoin subJoin) { List<Table> mainTables = new ArrayList<>(); if (subJoin.getJoinList() != null) { List<Table> list = processFromItem(subJoin.getLeft()); mainTables.addAll(list); mainTables = processJoins(mainTables, subJoin.getJoinList()); } return mainTables; } /** * 处理 joins * * @param mainTables 可以为 null * @param joins join 集合 * @return List 右连接查询的 Table 列表 */privateList<Table>processJoins(List<Table> mainTables,List<Join> joins){// join 表达式中最终的主表Table mainTable =null;// 当前 join 的左表Table leftTable =null;if(mainTables ==null){ mainTables =newArrayList<>();}elseif(mainTables.size()==1){ mainTable = mainTables.get(0); leftTable = mainTable;}//对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名Deque<List<Table>> onTableDeque =newLinkedList<>();for(Join join : joins){// 处理 on 表达式FromItem joinItem = join.getRightItem();// 获取当前 join 的表,subJoint 可以看作是一张表List<Table> joinTables =null;if(joinItem instanceofTable){ joinTables =newArrayList<>(); joinTables.add((Table) joinItem);}elseif(joinItem instanceofSubJoin){ joinTables =processSubJoin((SubJoin) joinItem);}if(joinTables !=null){// 如果是隐式内连接if(join.isSimple()){ mainTables.addAll(joinTables);continue;}// 当前表是否忽略Table joinTable = joinTables.get(0);List<Table> onTables =null;// 如果不要忽略,且是右连接,则记录下当前表if(join.isRight()){ mainTable = joinTable;if(leftTable !=null){ onTables =Collections.singletonList(leftTable);}}elseif(join.isLeft()){ onTables =Collections.singletonList(joinTable);}elseif(join.isInner()){if(mainTable ==null){ onTables =Collections.singletonList(joinTable);}else{ onTables =Arrays.asList(mainTable, joinTable);} mainTable =null;} mainTables =newArrayList<>();if(mainTable !=null){ mainTables.add(mainTable);}// 获取 join 尾缀的 on 表达式列表Collection<Expression> originOnExpressions = join.getOnExpressions();// 正常 join on 表达式只有一个,立刻处理if(originOnExpressions.size()==1&& onTables !=null){List<Expression> onExpressions =newLinkedList<>(); onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables)); join.setOnExpressions(onExpressions); leftTable = joinTable;continue;}// 表名压栈,忽略的表压入 null,以便后续不处理 onTableDeque.push(onTables);// 尾缀多个 on 表达式的时候统一处理if(originOnExpressions.size()>1){Collection<Expression> onExpressions =newLinkedList<>();for(Expression originOnExpression : originOnExpressions){List<Table> currentTableList = onTableDeque.poll();if(CollectionUtils.isEmpty(currentTableList)){ onExpressions.add(originOnExpression);}else{ onExpressions.add(builderExpression(originOnExpression, currentTableList));}} join.setOnExpressions(onExpressions);} leftTable = joinTable;}else{processOtherFromItem(joinItem); leftTable =null;}}return mainTables;}/** * 处理条件 * * @param currentExpression 当前 where 条件 * @param table 单个表 */protectedExpressionbuilderExpression(Expression currentExpression,Table table){returnthis.builderExpression(currentExpression,Collections.singletonList(table));}/** * 处理条件 * * @param currentExpression 当前 where 条件 * @param tables 多个表 */protectedExpressionbuilderExpression(Expression currentExpression,List<Table> tables){// 没有表需要处理直接返回if(CollectionUtils.isEmpty(tables)){return currentExpression;}// 第一步,获得 Table 对应的数据权限条件Expression dataPermissionExpression =null;for(Table table : tables){// 构建每个表的权限 Expression 条件Expression expression =buildDataPermissionExpression(table);if(expression ==null){continue;}// 合并到 dataPermissionExpression 中 dataPermissionExpression = dataPermissionExpression ==null? expression :newAndExpression(dataPermissionExpression, expression);}// 第二步,合并多个 Expression 条件if(dataPermissionExpression ==null){return currentExpression;}if(currentExpression ==null){return dataPermissionExpression;}// ① 如果表达式为 Or,则需要 (currentExpression) AND dataPermissionExpressionif(currentExpression instanceofOrExpression){returnnewAndExpression(newParenthesis(currentExpression), dataPermissionExpression);}// ② 如果表达式为 And,则直接返回 where AND dataPermissionExpressionreturnnewAndExpression(currentExpression, dataPermissionExpression);}/** * 构建指定表的数据权限的 Expression 过滤条件 * * @param table 表 * @return Expression 过滤条件 */privateExpressionbuildDataPermissionExpression(Table table){// 生成条件Expression allExpression =null;return allExpression;}}

拦截器定义好,下一步是将拦截器放置到mybatis的拦截器队列中。
定义一个公共的Configuration类:
DataPermissionConfiguration.java

@Configuration
public class DataPermissionConfiguration {

	/** 将自定义拦截器,添加到mybatis拦截器队列中。让拦截器生效*/
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(List<DataPermissionRule> dataPermissionRule) {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();

        // 分页插件
//        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());

        //添加权限拦截器。
        DataPermissionDatabaseInterceptor inner = new DataPermissionDatabaseInterceptor();
        List<InnerInterceptor> inners = new ArrayList<>(mybatisPlusInterceptor.getInterceptors());
        inners.add(0, inner);
        mybatisPlusInterceptor.setInterceptors(inners);

        return mybatisPlusInterceptor;
    }
    
}

完成上面配置后,每执行一个SQL都会被我们定义的拦截器。

你可能感兴趣的:(spring,boot,sql,后端)