利用 MyBatis Plus 拦截器动态管理数据访问权限
- 工作小总结&小工具类
- 时间:2024-11-17 16:37
- 527人已阅读
🔔🔔🔔好消息!好消息!🔔🔔🔔
有需要的朋友👉:联系凯哥
利用 MyBatis Plus 拦截器动态管理数据访问权限 功能权限与数据权限 在软件开发过程中,我们经常遇到需要根据用户角色来控制数据访问权限的需求。特别是在列表数据展示时,要确保用户只能查看其权限数据范围内的。本文将介绍一种通过MyBatis拦截器实现数据权限控制的方案,该方案灵活且易于集成到现有项目中。 数据权限分配 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface UserDataPermission { } 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.Data; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.EqualsAndHashCode; 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 @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) public class MyDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { private MyDataPermissionHandler 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)); } } private void setWhere(PlainSelect plainSelect, String whereSegment) { Expression sqlSegment = this.dataPermissionHandler.getSqlSegment(plainSelect, whereSegment); if (sqlSegment != null) { plainSelect.setWhere(sqlSegment); } } } import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.HexValue; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.PlainSelect; import java.lang.reflect.Method; @Slf4j public class MyDataPermissionHandler { private RemoteRoleService remoteRoleService; private RemoteUserService remoteUserService; @SneakyThrows(Exception.class) public Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) { Expression where = plainSelect.getWhere(); if (where == null) { where = new HexValue("1=1"); } log.info("开始进行权限过滤, where: {}, mappedStatementId: {}", where, whereSegment); String className = whereSegment.substring(0, whereSegment.lastIndexOf(".")); String methodName = whereSegment.substring(whereSegment.lastIndexOf(".") + 1); Table fromItem = (Table) plainSelect.getFromItem(); // 示例:根据用户角色生成不同的 SQL 片段 String roleName = "DATA_MANAGER"; // 假设从上下文中获取当前用户的角色 DataScope scope = DataPermission.getScope(List.of(roleName)); if (scope == DataScope.ALL) { return where; } else if (scope == DataScope.DEPT) { // 假设有一个字段 department_id Column column = new Column(new Table(fromItem.getName()), "department_id"); StringValue value = new StringValue("123"); // 假设从上下文中获取当前用户的部门ID return new EqualsTo(column, value); } else if (scope == DataScope.MYSELF) { // 假设有一个字段 user_id Column column = new Column(new Table(fromItem.getName()), "user_id"); StringValue value = new StringValue("456"); // 假设从上下文中获取当前用户的ID return new EqualsTo(column, value); } return where; } } import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class MyBatisPlusConfig { @Autowired private MyDataPermissionHandler myDataPermissionHandler; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); MyDataPermissionInterceptor dataPermissionInterceptor = new MyDataPermissionInterceptor(); dataPermissionInterceptor.setDataPermissionHandler(myDataPermissionHandler); interceptor.addInnerInterceptor(dataPermissionInterceptor); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } 功能权限与数据权限 import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public enum DataScope { ALL("ALL"), DEPT("DEPT"), MYSELF("MYSELF"); private String name; } import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Collection; @AllArgsConstructor @Getter public enum DataPermission { DATA_MANAGER("数据管理员", "DATA_MANAGER", DataScope.ALL), DATA_AUDITOR("数据审核员", "DATA_AUDITOR", DataScope.DEPT), DATA_OPERATOR("数据业务员", "DATA_OPERATOR", DataScope.MYSELF); private String name; private String code; private DataScope scope; public static String getName(String code) { for (DataPermission type : DataPermission.values()) { if (type.getCode().equals(code)) { return type.getName(); } } return null; } public static String getCode(String name) { for (DataPermission type : DataPermission.values()) { if (type.getName().equals(name)) { return type.getCode(); } } return null; } public static DataScope getScope(Collection<String> code) { for (DataPermission type : DataPermission.values()) { for (String v : code) { if (type.getCode().equals(v)) { return type.getScope(); } } } return DataScope.MYSELF; } } import cn.hutool.core.collection.CollectionUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.*; import net.sf.jsqlparser.expression.operators.relational.*; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.PlainSelect; 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 MyDataPermissionHandler { private RemoteRoleService remoteRoleService; private RemoteUserService remoteUserService; @SneakyThrows(Exception.class) public Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) { Expression where = plainSelect.getWhere(); if (where == null) { where = new HexValue("1=1"); } log.info("开始进行权限过滤, where: {}, mappedStatementId: {}", where, whereSegment); String className = whereSegment.substring(0, whereSegment.lastIndexOf(".")); String methodName = whereSegment.substring(whereSegment.lastIndexOf(".") + 1); Table fromItem = (Table) plainSelect.getFromItem(); // 示例:根据用户角色生成不同的 SQL 片段 String roleName = "DATA_MANAGER"; // 假设从上下文中获取当前用户的角色 DataScope scope = DataPermission.getScope(List.of(roleName)); if (scope == DataScope.ALL) { return where; } else if (scope == DataScope.DEPT) { // 假设有一个字段 department_id Column column = new Column(new Table(fromItem.getName()), "department_id"); StringValue value = new StringValue("123"); // 假设从上下文中获取当前用户的部门ID return new EqualsTo(column, value); } else if (scope == DataScope.MYSELF) { // 假设有一个字段 user_id Column column = new Column(new Table(fromItem.getName()), "user_id"); StringValue value = new StringValue("456"); // 假设从上下文中获取当前用户的ID return new EqualsTo(column, value); } return where; } } import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Constants; import org.apache.ibatis.annotations.Param; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; public interface DataPermissionMapper<T> extends BaseMapper<T> { @Override @UserDataPermission T selectById(Serializable id); @Override @UserDataPermission List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); @Override @UserDataPermission List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); @Override @UserDataPermission Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Override @UserDataPermission List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Override @UserDataPermission List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Override @UserDataPermission List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Override @UserDataPermission <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Override @UserDataPermission <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); } 通过上述步骤,我们可以有效地使用 MyBatis Plus 拦截器实现数据权限控制。无论是基础版本还是进阶版,都能满足不同场景下的需求。希望本文能帮助你在实际项目中更好地管理和控制数据权限。引言
首先,我们需要创建一个自定义注解 @UserDataPermission,用于标记需要进行数据权限控制的方法或类。具体代码如下:
接下来,创建一个拦截器 MyDataPermissionInterceptor,实现 InnerInterceptor 接口,并重写查询方法。代码如下:
创建一个处理类 MyDataPermissionHandler,用于生成数据权限的 SQL 片段。如下代码:
最后,将拦截器加入 MyBatis-Plus 插件中。具体入下代码:
定义一个范围枚举 DataScope,用于表示不同的数据权限范围。如下示例:
定义一个角色枚举 DataPermission,用于表示不同的角色及其对应的数据权限范围。代码如下:
在 MyDataPermissionHandler 中重写 getSqlSegment 方法,根据角色生成不同的 SQL 片段。完整代码:
在实际项目中,我们可以在 Mapper 层的方法上添加 @UserDataPermission 注解,实现数据权限控制。
上一篇: Github优质项目推荐合集