基于mybatis-plus interceptor的数据权限以及自定义injector整合baseMapper

1.自定义interceptor做数据权限

  1. 自定义dataScope
@Data
@EqualsAndHashCode(callSuper = true)
public class DataScope extends HashMap {
    /**
     * 限制范围的字段名称 (除个人外)
     */
    private String scopeName = "org_id";
    /**
     * 限制范围为个人时的字段名称
     */
    private String selfScopeName = "user_id";

    private String selfName = "create_by";
    /**
     * 当前用户ID
     */
    private Integer userId;

    /**
     * 具体的数据范围
     */
    private List<Long> orgIds;

    private DataPermissionType Type;
    /**
     * 限制范围为插入时的类型
     */
    private String dataType = "type";

}

@Getter
@AllArgsConstructor
@ApiModel(value = "DataScopeType", description = "数据权限类型-枚举")
public enum DataScopeType implements BaseEnum {

    /**
     * ALL=5全部
     */
    ALL(5, "全部"),
    /**
     * THIS_LEVEL=4本级
     */
    THIS_LEVEL(4, "本级"),
    /**
     * THIS_LEVEL_CHILDREN=3本级以及子级
     */
    THIS_LEVEL_CHILDREN(3, "本级以及子级"),
    /**
     * CUSTOMIZE=2自定义
     */
    CUSTOMIZE(2, "自定义"),
    /**
     * SELF=1个人
     */
    SELF(1, "个人"),
    ;

    @ApiModelProperty(value = "描述")
    private final int val;

    private final String desc;


    public static DataScopeType match(String val, DataScopeType def) {
        return Stream.of(values()).parallel().filter((item) -> item.name().equalsIgnoreCase(val)).findAny().orElse(def);
    }

    public static DataScopeType match(Integer val, DataScopeType def) {
        return Stream.of(values()).parallel().filter((item) -> val != null && item.getVal() == val).findAny().orElse(def);
    }

    public static DataScopeType get(String val) {
        return match(val, null);
    }

    public static DataScopeType get(Integer val) {
        return match(val, null);
    }

    public boolean eq(final DataScopeType val) {
        return val != null && eq(val.name());
    }

    @Override
    @ApiModelProperty(value = "编码", allowableValues = "ALL,THIS_LEVEL,THIS_LEVEL_CHILDREN,CUSTOMIZE,SELF", example = "ALL")
    public String getCode() {
        return this.name();
    }
    public static DataScopeType getEnum(String desc) {
        for (DataScopeType value : DataScopeType.values()) {
            if (value.getDesc().equals(desc)) {
                return value;
            }
        }
        return null;
    }
    public static String getType(Integer code) {
        DataScopeType[] values = DataScopeType.values();
        for (DataScopeType value : values) {
            if (code.equals(value.getCode())) {
                return value.getDesc();
            }
        }
        return "";
    }

}
  1. 自定义Interceptor
@Slf4j
@AllArgsConstructor
public class DataScopeInnerInterceptor implements InnerInterceptor {
    private final Function<Integer, Map<String, Object>> function;


    /**
     * 查找参数是否包括DataScope对象
     *
     * @param parameterObj 参数列表
     * @return DataScope
     */
    protected Optional<DataScope> findDataScope(Object parameterObj) {
        if (parameterObj == null) {
            return Optional.empty();
        }
        if (parameterObj instanceof DataScope) {
            return Optional.of((DataScope) parameterObj);
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return Optional.of((DataScope) val);
                }
            }
        }
        return Optional.empty();
    }
    /**
    *其原理就是sql外包装一层查询
    **/    
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        DataScope dataScope = findDataScope(parameter).orElse(null);
        if (dataScope == null) {
            return;
        }

        String originalSql = boundSql.getSql();

        String scopeName = dataScope.getScopeName();
        String selfScopeName = dataScope.getSelfScopeName();
        String typeScopeName = dataScope.getDataType();
        DataPermissionType dataType = dataScope.getType();
        Integer userId = dataScope.getUserId() == null ? ContextUtil.getUserId().intValue() : dataScope.getUserId();
        List<Long> orgIds = dataScope.getOrgIds();
        DataScopeType dsType = DataScopeType.SELF;

        if (CollectionUtil.isEmpty(orgIds)) {
            //查询当前用户的 角色 最小权限
            //userId

            //dsType orgIds
            Map<String, Object> result = function.apply(userId);
            if (result == null) {
                return;
            }

            Integer type = (Integer) result.get("dsType");
            dsType = DataScopeType.get(type);
            orgIds = (List<Long>) result.get("orgIds");
        }
            //查全部
            if (DataScopeType.ALL.eq(dsType)) {
                return;
            }
            //查个人
            if (DataScopeType.SELF.eq(dsType)) {
                originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + dataScope.getSelfName() + " = " + userId;
            }
            //查其他
            else if (StrUtil.isNotBlank(scopeName) && CollUtil.isNotEmpty(orgIds)) {
                String join = CollectionUtil.join(orgIds, ",");
                originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
            }



        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
        mpBoundSql.sql(originalSql);
    }

}
  1. 添加自定义interceptor进mybatis
protected List<InnerInterceptor> getPaginationBeforeInnerInterceptor() {
    List<InnerInterceptor> list = new ArrayList<>();
    Boolean isDataScope = databaseProperties.getIsDataScope();
    if (isDataScope) {
        // todo UserService.class 改方法是查询当前用户的组织以及角色权限等信息
        list.add(new DataScopeInnerInterceptor(userId -> userServiceApi.getDataScope(userId)));
    }
    return list;
}

MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
List<InnerInterceptor> beforeInnerInterceptor = getPaginationBeforeInnerInterceptor();
if (!beforeInnerInterceptor.isEmpty()) {
    beforeInnerInterceptor.forEach(interceptor::addInnerInterceptor);
}
  1. 添加interceptor进入sqlSessionFactory

@Configuration
public class DataSourceConfiguration {

    //会依赖注入Spring容器中所有的mybatis的Interceptor拦截器
    @Autowired(required = false)
    private Interceptor[] interceptors;

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean
    public SqlSessionFactory dataHubSqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisConfiguration configuration = new MybatisConfiguration();
//        configuration.addInterceptor(paginationInterceptor());

        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setDataSource(dataSource);
        //如果自定义sqlFactoryBean加入这个自定义的mybatis拦截器才会生效
        sqlSessionFactoryBean.setPlugins(interceptors);
        //设置globalConfig自定义注入器才会生效
        sqlSessionFactoryBean.setGlobalConfig(getGlobalConfig());
        return sqlSessionFactoryBean.getObject();
    }
    @Bean
    public GlobalConfig getGlobalConfig() {

        if (this.applicationContext.getBeanNamesForType(MySqlInjector.class, false, false).length > 0 ) {
            LampSqlInjector lampSqlInjector = applicationContext.getBean(MySqlInjector.class);
            GlobalConfig globalConfig = new GlobalConfig().setSqlInjector(mySqlInjector);
            return globalConfig;
        }
        return new GlobalConfig();
    }

}

这种情况如果使用mybatis-plus框架无法使用其baseMapper的一些基础方法比如selectList等等,我们可以自定义mybaits-plus injector来实现

2.自定义injector配合数据权限使用

  1. 首先自定义通用Mapper
public interface SuperMapper<T> extends BaseMapper<T> {

    /**模仿BaseMapper做的带数据权限查询的接口
     * @Author sff
     * @Description
     * @Date 13:57 2021/9/9
     * @Param [queryWrapper, dataScope]
     * @Return java.util.List<T>
     */
    List<T> selectListByScope(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope dataScope);

    /**
     * 通用数据权限分页查询
     *
     * @Author sff
     * @Description
     * @Date 17:19 2021/9/6
     * @Param [page, queryWrapper, dataScopeType]
     * @Return E
     */
    <E extends IPage<T>> E selectPageByScope(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope dataScope);
}
  1. 自定义selectListByScepe
/**
 * 相当于selectList的重载,加了数据权限
 * @Author sff
 * @Description
 * @Date 14:45 2021/9/9
 */
public class SelectListByScope extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        //给sql脚本赋值
        SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
        //给sql脚本赋值
        String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
                sqlWhereEntityWrapper(true, tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, "selectListByScope", sqlSource, tableInfo);

    }

}

public class SelectPageByScope extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.SELECT_PAGE;
        String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true),
                tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, "selectPageByScope", sqlSource, tableInfo);
    }
}

MappedStatement对象:里封装了XML当中一个标签的所有信息,主要用于保存配置的SQL语句。
示例: insert into c_table_permission (data_id, org_id, is_query, is_visible) values (#{item.tableId}, #{orgId}, #{item.isQuery}, #{item.isVisible})

3. 自定义mybtis注入器

public class MySqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);

        //增加自定义方法
        methodList.add(new SelectListByScope());
        methodList.add(new SelectPageByScope());
        return methodList;
    }
}
  1. 增加配置类将SQL注入器添加到容器
  @Bean    
  @ConditionalOnMissingBean    
  public MySqlInjector getMySqlInjector() {
          return new MySqlInjector();
 }
  1. 重复interceptor步骤(如果自定义了sqlSessionFactoryBean则需要手动放到sqlSessionFactory容器中)

@Configuration
public class DataSourceConfiguration {

    //会依赖注入Spring容器中所有的mybatis的Interceptor拦截器
    @Autowired(required = false)
    private Interceptor[] interceptors;

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean
    public SqlSessionFactory dataHubSqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisConfiguration configuration = new MybatisConfiguration();
//        configuration.addInterceptor(paginationInterceptor());

        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setDataSource(dataSource);
        //如果自定义sqlFactoryBean加入这个自定义的mybatis拦截器才会生效
        sqlSessionFactoryBean.setPlugins(interceptors);
        //设置globalConfig自定义注入器才会生效
        sqlSessionFactoryBean.setGlobalConfig(getGlobalConfig());
        return sqlSessionFactoryBean.getObject();
    }
    @Bean
    public GlobalConfig getGlobalConfig() {

        if (this.applicationContext.getBeanNamesForType(MySqlInjector.class, false, false).length > 0 ) {
            LampSqlInjector lampSqlInjector = applicationContext.getBean(MySqlInjector.class);
            GlobalConfig globalConfig = new GlobalConfig().setSqlInjector(mySqlInjector);
            return globalConfig;
        }
        return new GlobalConfig();
    }

}

3.实际应用


@Service
public class EventServiceImpl extends ServiceImpl<EventMapper, Event> implements EventService {
     @Override
    public IPage<Event> permissionList(User user) {
        DataScope dataScope = new DataScope();
        dataScope.setUserId(user.getId());
        IPage<Event> eventIPage = new Page<>(1, 10);
        IPage<Event> events = baseMapper.selectPageByScope(eventIPage, Wraps.<Event>lbQ()
                .eq(Event::getWorkspaceId, user.getWorkspaceId())
                .orderByDesc(Event::getUpdateTime), dataScope);
 //       List<Event> eventList = baseMapper.selectListByScope(Wraps.<Event>lbQ().eq(Event::getWorkspaceId, user.getWorkspaceId()).orderByAsc(Event::getCreateBy), dataScope);
        return events;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus是一个在MyBatis基础上进行功能扩展的数据库ORM框架。它提供了自动生成代码的功能,通过定义自定义引擎文件(VM文件),我们可以对生成的代码进行定制和扩展。 首先,我们需要在项目中创建一个.vm文件,该文件是一个Velocity模板文件,用于生成具体的代码。我们可以根据自己的需求定义不同的模板,然后通过配置MyBatis-Plus,告诉它使用我们自定义的模板。 在.vm文件中,我们可以使用Velocity语法来定义生成代码的格式和内容。例如,我们可以定义实体类的字段名、类型、注释等信息,以及对应的数据库表名、列名等信息。另外,我们还可以定义生成的文件路径、文件名等。 当配置好自定义引擎文件后,我们需要在MyBatis-Plus的代码生成配置中指明使用哪个引擎文件。配置项包括模板路径、模板名称等。在运行代码生成命令时,MyBatis-Plus会根据配置的引擎文件,生成相应的代码文件。 通过自定义引擎文件,我们可以灵活地控制代码生成的结果,满足不同项目的需求。我们可以根据实际情况,修改生成的代码的格式、结构或增加一些自定义的内容。这使得生成的代码更符合我们项目的规范和要求。 总结来说,自定义引擎文件是MyBatis-Plus自动生成代码功能的关键部分。通过将我们自定义的模板文件与MyBatis-Plus的配置进行合理结合,可以实现对生成的代码进行个性化定制,提高开发效率和代码质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值