【SpringBoot整合mybatis-plus实现自定义拦截器-使用自定义注解】

自定义注解

package com.scm.common.datascope.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CompanyDataScope {
    /**
     * 服务商别名- 对应业务表字段名称
     */
    public String serviceAlias() default "";

    /**
     * 客户别名- 对应业务表字段名称
     */
    public String customerAlias() default "";
}

第一步:实现MP自带拦截器 [InnerInterceptor]

package com.scm.common.datascope.interceptor;

import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.*;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.SQLException;
import java.util.List;


@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CompanyDataInterceptor extends JsqlParserSupport implements InnerInterceptor {

    /**
     * 数据权限处理器
     */
    private CompanyDataHandler 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;
        }
        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);
        }
    }
}

第二步: 拦截后数据处理方式

package com.scm.common.datascope.interceptor;

import cn.hutool.core.stream.CollectorUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.scm.common.core.enums.CompanyData;
import com.scm.common.core.enums.DataScope;
import com.scm.common.datascope.annotation.CompanyDataScope;
import com.scm.common.security.utils.SecurityUtils;
import com.scm.system.api.model.LoginUser;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class CompanyDataHandler {
    /**
     *
     * @param plainSelect  查询对象
     * @param whereSegment 查询条件片段
     * @return JSqlParser 条件表达式
     */
    @SneakyThrows(Exception.class)
    public Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) {
        // 待执行 SQL Where 条件表达式
        Expression where = plainSelect.getWhere();
        if (where == null) {
            where = new HexValue(" 1 = 1 ");
        }
        log.info("开始进行权限过滤,where: {},mappedStatementId: {}", where, whereSegment);
        //获取mapper名称
        String className = whereSegment.substring(0, whereSegment.lastIndexOf("."));
        //获取方法名
        String methodName = whereSegment.substring(whereSegment.lastIndexOf(".") + 1);
        Table fromItem = (Table) plainSelect.getFromItem();
        // 有别名用别名,无别名用表名,防止字段冲突报错
        Alias fromItemAlias = fromItem.getAlias();
        String mainTableName = fromItemAlias == null ? fromItem.getName() : fromItemAlias.getName();
        //获取当前mapper 的方法
        Method[] methods = Class.forName(className).getMethods();
        //遍历判断mapper 的所以方法,判断方法上是否有 CompanyDataScope
        for (Method m : methods) {
            if (Objects.equals(m.getName(), methodName)) {
                CompanyDataScope annotation = m.getAnnotation(CompanyDataScope.class);
                if (annotation == null) {
                    return where;
                }
                // 1、当前用户
                LoginUser loginUser = SecurityUtils.getLoginUser();
                Set<String> dataScope= loginUser.getDataScope().stream().sorted().collect(Collectors.toSet());
                //如果数据权限为空则没有权限
                if ( dataScope.isEmpty()) return where;
                //2.获取用户对应企业数据权限
                Long companyId = loginUser.getSysUser().getScmCompanyId();
                //如果企业id为空则没有权限
                if (companyId == null) return where;
               // Set<String> roleTypeSet =Sets.newHashSet("7");
                DataScope scopeType = CompanyData.getScope(dataScope);
                switch (scopeType) {
                    // 查看全部
                    case ALL:
                        return where;
                    case DEPT:
                        // 查看本服务商用户数据
                        // 创建IN 表达式
                        // 创建IN范围的元素集合
                        List<String> companyList = Lists.newArrayList(String.valueOf(companyId));
                        // 把集合转变为JSQLParser需要的元素列表
                        ItemsList deptList = new ExpressionList(companyList.stream().map(StringValue::new).collect(Collectors.toList()));
                        InExpression inExpressiondept = new InExpression(new Column(mainTableName + "." + annotation.serviceAlias()), deptList);
                        return new AndExpression(where, inExpressiondept);
                    case MYSELF:
                        // 查看客户的数据
                        //  = 表达式
                        EqualsTo usesEqualsTo = new EqualsTo();
                        usesEqualsTo.setLeftExpression(new Column(mainTableName + "." + annotation.customerAlias()));
                        usesEqualsTo.setRightExpression(new StringValue(String.valueOf(companyId)));
                        return new AndExpression(where, usesEqualsTo);
                    default:
                        break;
                }
            }

        }
        //说明无权查看,
        where = new HexValue(" 1 = 2 ");
        return where;
    }


}

第三步: 配置中心注入自定义拦截器

package com.scm.common.datascope.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.scm.common.datascope.interceptor.CompanyDataHandler;
import com.scm.common.datascope.interceptor.CompanyDataInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class MybatisConfig {
    /**
     * mybatis-plus分页插件配置
     *
     * @return PaginationInterceptor
     */
    @Bean(name = "mybatisPlusInterceptor")
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
	/**
	*注入自定义拦截器
	*/
    @Bean
    public CompanyDataInterceptor myInterceptor(MybatisPlusInterceptor mybatisPlusInterceptor) {
        CompanyDataInterceptor sql = new CompanyDataInterceptor();
        sql.setDataPermissionHandler(new CompanyDataHandler());
        List<InnerInterceptor> list = new ArrayList<>();
        // 添加数据权限插件
        list.add(sql);
        // 分页插件
        mybatisPlusInterceptor.setInterceptors(list);
        list.add(new PaginationInnerInterceptor(DbType.MYSQL));
        return sql;
    }
}

枚举的方式获取拦截权限

package com.scm.common.core.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Collection;

@AllArgsConstructor
@Getter
public enum CompanyData {

    // 枚举类型根据范围从前往后排列,避免影响getScope
    // Scope 数据权限范围 : ALL(全部)、DEPT(服务商)、MYSELF(客户)
    DATA_MANAGER("运营", "6", DataScope.ALL),
    DATA_AUDITOR("服务商", "7",DataScope.DEPT),
    DATA_OPERATOR("客户", "8",DataScope.MYSELF);

    private String name;
    private String code;
    private DataScope scope;


    public static String getName(String code) {
        for (CompanyData type : CompanyData.values()) {
            if (type.getCode().equals(code)) {
                return type.getName();
            }
        }
        return null;
    }

    public static String getCode(String name) {
        for (CompanyData type : CompanyData.values()) {
            if (type.getName().equals(name)) {
                return type.getCode();
            }
        }
        return null;
    }

    public static DataScope getScope(Collection<String> code) {
        for (CompanyData type : CompanyData.values()) {
            for (String v : code) {
                if (type.getCode().equals(v)) {
                    return type.getScope();
                }
            }
        }
        return DataScope.MYSELF;
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
系统地讲解了如何构建一个日常生产环境实用的基于Spring Boot并且集成springmvc + shiro + mybatis-plus + beetl的后台管理系统,可管理代码生成模版,管理连接生成代码的数据库. Guns框架自带的功能:1.用户管理 2.角色管理 3.部门管理 4.菜单管理 5.字典管理 6.业务日志 7.登录日志 8.监控管理 9.通知管理 10.代码生成 Guns特点: 1. 基于SpringBoot,简化了大量项目配置和maven依赖,让您更专注于业务开发,独特的分包方式,代码多而不乱。 2. 完善的日志记录体系,可记录登录日志,业务操作日志(可记录操作前和操作后的数据),异常日志到数据库,通过@BussinessLog注解和LogObjectHolder.me().set()方法,业务操作日志可具体记录哪个用户,执行了哪些业务,修改了哪些数据,并且日志记录为异步执行,详情请见@BussinessLog注解和LogObjectHolder,LogManager,LogAop类。 3. 利用beetl模板引擎对前台页面进行封装和拆分,使臃肿的html代码变得简洁,更加易维护。 4. 对常用js插件进行二次封装,使js代码变得简洁,更加易维护,具体请见webapp/static/js/common文件夹内js代码。 5. 利用ehcache框架对经常调用的查询进行缓存,提升运行速度,具体请见ConstantFactory类中@Cacheable标记的方法。 6. controller层采用map + warpper方式的返回结果,返回给前端更为灵活的数据,具体参见com.stylefeng.guns.modular.system.warpper包中具体类。 7. 防止XSS攻击,通过XssFilter类对所有的输入的非法字符串进行过滤以及替换。 8. 简单可用的代码生成体系,通过SimpleTemplateEngine可生成带有主页跳转和增删改查的通用控制器、html页面以及相关的js,还可以生成Service和Dao,并且这些生成项都为可选的,通过ContextConfig下的一些列xxxSwitch开关,可灵活控制生成模板代码,让您把时间放在真正的业务上。 9. 控制器层统一的异常拦截机制,利用@ControllerAdvice统一对异常拦截,具体见com.stylefeng.guns.core.aop.GlobalExceptionHandler类。 10. 页面统一的js key-value单例模式写法,每个页面生成一个唯一的全局变量,提高js的利用效率,并且有效防止多个人员开发引起的函数名/类名冲突,并且可以更好地去维护代码
要配置自定义拦截器拦截指定目录的 mapper 文件,可以按照以下步骤进行: 1. 创建一个自定义拦截器类,实现 Mybatis 的 Interceptor 接口,重写其中的 intercept() 方法,在该方法中实现自定义的拦截逻辑。 2. 在该拦截器类上使用 @Intercepts 注解定义需要拦截的方法以及拦截的时机,例如: ```java @Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class MyInterceptor implements Interceptor { // ... } ``` 以上示例中,定义了拦截 Executor 类中的 query() 和 update() 方法,并传入了对应的参数类型。 3. 在 Mybatis 的配置文件中,将该拦截器类添加到插件列表中,并指定需要拦截的 mapper 文件路径,例如: ```xml <configuration> <plugins> <plugin interceptor="com.example.MyInterceptor"> <property name="mapperPath" value="com/example/mapper/**/*Mapper.xml"/> </plugin> </plugins> </configuration> ``` 以上示例中,将 MyInterceptor 添加到插件列表中,并指定需要拦截的 mapper 文件路径为 com/example/mapper/ 目录下的所有 Mapper.xml 文件。 在 MyInterceptor 类中,可以通过获取配置文件中的 mapperPath 属性来获取需要拦截的 mapper 文件路径,然后根据该路径判断是否需要拦截当前执行的方法。 ```java public class MyInterceptor implements Interceptor { private String mapperPath; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String mapperId = mappedStatement.getId(); if (mapperId.matches(mapperPath)) { // 需要拦截的方法 } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { mapperPath = properties.getProperty("mapperPath"); } } ``` 以上示例中,通过获取配置文件中的 mapperPath 属性,并使用正则表达式判断当前执行的方法是否需要拦截。如果需要拦截,则执行自定义的拦截逻辑。否则,直接调用被拦截方法的原始逻辑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值