自定义一个AllbsDataPermissionHandler,使用mybatis plus自带的是可以的,主要考虑到后面还有其他自定义效果,所以单独写了一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.schema.Table;
public interface AllbsDataPermissionHandler {
Expression getSqlSegment(final Table table, Expression where, String mappedStatementId); }
|
将mybatis plus中的PluginUtils
工具类抽出使用,主要是为了解决其中方法jdk17不兼容的问题
当然以下代码还没改,后续更改了之后文档再更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.apache.ibatis.session.Configuration;
import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map;
public abstract class PluginUtils { public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";
@SuppressWarnings("unchecked") public static <T> T realTarget(Object target) { if (Proxy.isProxyClass(target.getClass())) { MetaObject metaObject = SystemMetaObject.forObject(target); return realTarget(metaObject.getValue("h.target")); } return (T) target; }
public static void setAdditionalParameter(BoundSql boundSql, Map<String, Object> additionalParameters) { additionalParameters.forEach(boundSql::setAdditionalParameter); }
public static PluginUtils.MPBoundSql mpBoundSql(BoundSql boundSql) { return new PluginUtils.MPBoundSql(boundSql); }
public static PluginUtils.MPStatementHandler mpStatementHandler(StatementHandler statementHandler) { statementHandler = realTarget(statementHandler); MetaObject object = SystemMetaObject.forObject(statementHandler); return new PluginUtils.MPStatementHandler(SystemMetaObject.forObject(object.getValue("delegate"))); }
public static class MPStatementHandler { private final MetaObject statementHandler;
MPStatementHandler(MetaObject statementHandler) { this.statementHandler = statementHandler; }
public ParameterHandler parameterHandler() { return get("parameterHandler"); }
public MappedStatement mappedStatement() { return get("mappedStatement"); }
public Executor executor() { return get("executor"); }
public PluginUtils.MPBoundSql mPBoundSql() { return new PluginUtils.MPBoundSql(boundSql()); }
public BoundSql boundSql() { return get("boundSql"); }
public Configuration configuration() { return get("configuration"); }
@SuppressWarnings("unchecked") private <T> T get(String property) { return (T) statementHandler.getValue(property); } }
public static class MPBoundSql { private final MetaObject boundSql; private final BoundSql delegate;
MPBoundSql(BoundSql boundSql) { this.delegate = boundSql; this.boundSql = SystemMetaObject.forObject(boundSql); }
public String sql() { return delegate.getSql(); }
public void sql(String sql) { boundSql.setValue("sql", sql); }
public List<ParameterMapping> parameterMappings() { List<ParameterMapping> parameterMappings = delegate.getParameterMappings(); return new ArrayList<>(parameterMappings); }
public void parameterMappings(List<ParameterMapping> parameterMappings) { boundSql.setValue("parameterMappings", Collections.unmodifiableList(parameterMappings)); }
public Object parameterObject() { return get("parameterObject"); }
public Map<String, Object> additionalParameters() { return get("additionalParameters"); }
@SuppressWarnings("unchecked") private <T> T get(String property) { return (T) boundSql.getValue(property); } } }
|
自定义一个注解,用于标注实体类中的需要过滤的字段
目前只取获取到的第一个字段,你要是标多个我也没办法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.lang.annotation.*;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface ScopeField {
String value() default ""; }
|
数据权限处理器
用于传递重新拼sql时所需的一些参数,新增的情况暂时还未处理,后续继续更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; 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.schema.Table; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.insert.Insert; 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 net.sf.jsqlparser.statement.update.Update; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType;
import java.sql.Connection; import java.util.List;
@Data @NoArgsConstructor @AllArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @SuppressWarnings({"rawtypes"}) public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { private AllbsDataPermissionHandler dataPermissionHandler;
@Override 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 || sct == SqlCommandType.SELECT) { if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { return; } PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); mpBs.sql(parserMulti(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)); } }
@Override protected void processInsert(Insert insert, int index, String sql, Object obj) { throw new UnsupportedOperationException(); }
@Override protected void processDelete(Delete delete, int index, String sql, Object obj) { final Expression sqlSegment = getUpdateOrDeleteExpression(delete.getTable(), delete.getWhere(), (String) obj); if (null != sqlSegment) { delete.setWhere(sqlSegment); } }
@Override protected void processUpdate(Update update, int index, String sql, Object obj) { final Expression sqlSegment = getUpdateOrDeleteExpression(update.getTable(), update.getWhere(), (String) obj); if (null != sqlSegment) { update.setWhere(sqlSegment); } }
protected void setWhere(PlainSelect plainSelect, String whereSegment) { Table table = (Table) plainSelect.getFromItem(); Expression sqlSegment = dataPermissionHandler.getSqlSegment(table, plainSelect.getWhere(), whereSegment); if (null != sqlSegment) { plainSelect.setWhere(sqlSegment); } }
protected Expression getUpdateOrDeleteExpression(final Table table, final Expression where, final String whereSegment) { return dataPermissionHandler.getSqlSegment(table, where, whereSegment); } }
|
核心,进行sql重新封装的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| import cn.allbs.allbsjwt.config.utils.SecurityUtils; import cn.allbs.allbsjwt.config.vo.SysUser; import cn.allbs.common.constant.StringPool; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; 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.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table;
import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set;
@Slf4j public class CustomPermissionHandler implements AllbsDataPermissionHandler {
private final static String DEFAULT_FILTER_FIELD = "ent_id";
@Override public Expression getSqlSegment(final Table table, Expression where, String mappedStatementId) {
SysUser sysUser = SecurityUtils.getUser(); if (sysUser == null) { return where; } Set<Long> permissionEntList = sysUser.getEntIdList();
TableInfo tableInfo = TableInfoHelper.getTableInfo(table.getName()); String fieldName = tableInfo.getFieldList().stream() .filter(a -> a.getField().getAnnotation(ScopeField.class) != null) .map(a -> a.getField().getAnnotation(ScopeField.class).value()) .findFirst() .orElse(DEFAULT_FILTER_FIELD); Alias fromItemAlias = table.getAlias(); String finalFieldName = Optional.ofNullable(fromItemAlias).map(a -> a.getName() + StringPool.DOT + fieldName).orElse(fieldName);
if (permissionEntList.size() > 1) { InExpression inExpression = new InExpression(new Column(finalFieldName), getItemList(permissionEntList));
return where == null ? inExpression : new AndExpression(where, inExpression); } EqualsTo equalsTo = new EqualsTo(); equalsTo.setLeftExpression(new Column(finalFieldName)); equalsTo.setRightExpression(new LongValue(permissionEntList.stream().findFirst().orElse(0L))); return where == null ? equalsTo : new AndExpression(where, equalsTo); }
private ItemsList getItemList(Set<Long> permissionEntList) { List<Expression> list = new LinkedList<>(); for (Long aLong : permissionEntList) { try { Expression expression = CCJSqlParserUtil.parseCondExpression(StrUtil.join(",", aLong)); list.add(expression); } catch (JSQLParserException e) { log.error("筛选数据转换为表达式失败!" + e.getLocalizedMessage()); } } return new ExpressionList(list); } }
|
最后将改插件注册进mybatis plus即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class MybatisPlusCustomConfig {
@Bean public MybatisPlusInterceptor customMybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); DataPermissionInterceptor dataPermissionInterceptor = new DataPermissionInterceptor(new CustomPermissionHandler()); interceptor.addInnerInterceptor(dataPermissionInterceptor); return interceptor; } }
|
说明
-
所有sql将默认拦截并自动拼接筛选条件
-
如果想要对部分sql不进行筛选,则在dao层的类名上添加注解@InterceptorIgnore
或者dao层的某个方法上添加注解@InterceptorIgnore
-
如果想要使用mybatis plus自带的sdk并使其中某些方法不进行数据筛选,则重新写一个接口集成BaseMapper,单独对其中的某个方法添加注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import com.baomidou.mybatisplus.annotation.InterceptorIgnore; 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.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Component;
import java.io.Serializable; import java.util.List;
@Mapper @Component public interface DataScopeMapper<T> extends BaseMapper<T> {
@Override @InterceptorIgnore T selectById(Serializable id);
@Override List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
@Override <P extends IPage<T>> P selectPage(P page, @Param( Constants.WRAPPER) Wrapper<T> queryWrapper); }
|
- 可以进行功能反转,比如目前是全部sql拦截,添加注解不拦截。可以修改
beforePrepare
使其默认全部不拦截,添加了该注解的方法拦截sql并重新组装。
- 如果不想违反mybatis plus注解本意,可以重新自定义一个注解来达成上述效果,本文不做展示,参考上一篇即可实现。
添加新增、更新、批量新增、批量更新的数据过滤
使用
AllbsDataPermissionHandler
添加两个方法用于处理新增和更新。具体实现效果看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
void insertParameter(Insert insertStmt, BoundSql boundSql);
void updateParameter(Update updateStmt, MappedStatement mappedStatement, BoundSql boundSql);
|
CustomPermissionHandler
的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| package cn.allbs.allbsjwt.config.datascope.mapper;
import cn.allbs.allbsjwt.config.utils.SecurityUtils; import cn.allbs.allbsjwt.config.vo.SysUser; import cn.allbs.common.constant.StringPool; import cn.allbs.mybatis.datascope.DataPmsHandler; import cn.allbs.mybatis.datascope.ScopeField; import cn.allbs.mybatis.execption.UserOverreachException; import cn.allbs.mybatis.utils.PluginUtils; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.InExpression; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.update.Update; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.springframework.stereotype.Component;
import java.util.List; import java.util.Optional; import java.util.Set;
@Slf4j @Component public class CustomPermissionHandler implements DataPmsHandler {
private final static String DEFAULT_FILTER_FIELD = "ent_id";
@Override public Expression getSqlSegment(final Table table, Expression where, String mappedStatementId) {
SysUser sysUser = SecurityUtils.getUser(); if (sysUser == null) { return where; } Set<Long> permissionEntList = sysUser.getEntIdList();
TableInfo tableInfo = TableInfoHelper.getTableInfo(table.getName()); String fieldName = tableInfo.getFieldList().stream() .filter(a -> a.getField().getAnnotation(ScopeField.class) != null) .map(a -> a.getField().getAnnotation(ScopeField.class).value()) .findFirst() .orElse(DEFAULT_FILTER_FIELD); Alias fromItemAlias = table.getAlias(); String finalFieldName = Optional.ofNullable(fromItemAlias).map(a -> a.getName() + StringPool.DOT + fieldName).orElse(fieldName);
if (permissionEntList.size() > 1) { InExpression inExpression = new InExpression(new Column(finalFieldName), PluginUtils.getItemList(permissionEntList));
return where == null ? inExpression : new AndExpression(where, inExpression); } EqualsTo equalsTo = new EqualsTo(); equalsTo.setLeftExpression(new Column(finalFieldName)); equalsTo.setRightExpression(new LongValue(permissionEntList.stream().findFirst().orElse(0L))); return where == null ? equalsTo : new AndExpression(where, equalsTo); }
@Override public void updateParameter(Update updateStmt, MappedStatement mappedStatement, BoundSql boundSql) { TableInfo tableInfo = TableInfoHelper.getTableInfo(updateStmt.getTable().getName()); parameterHandler(tableInfo.getFieldList(), boundSql); }
@Override public void insertParameter(Insert insertStmt, BoundSql boundSql) { TableInfo tableInfo = TableInfoHelper.getTableInfo(insertStmt.getTable().getName()); parameterHandler(tableInfo.getFieldList(), boundSql); }
private void parameterHandler(List<TableFieldInfo> fieldList, BoundSql boundSql) { SysUser sysUser = SecurityUtils.getUser(); if (sysUser.getId() == 1L) { return; } Set<Long> permissionEntList = sysUser.getEntIdList();
String fieldName = fieldList.stream() .filter(a -> a.getField().getAnnotation(ScopeField.class) != null) .map(a -> a.getField().getAnnotation(ScopeField.class).value()) .findFirst() .orElse(DEFAULT_FILTER_FIELD);
MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject());
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) { String propertyName = parameterMapping.getProperty(); if (propertyName.startsWith("ew.paramNameValuePairs")) { continue; } String[] arr = propertyName.split("\\."); String propertyNameTrim = arr[arr.length - 1].replace("_", "").toUpperCase(); if (fieldName.replaceAll("[._\\-$]", "").toUpperCase().equals(propertyNameTrim)) { if (!Optional.ofNullable(metaObject.getValue(propertyName)).isPresent()) { return; } long currentEntId = Long.parseLong(metaObject.getValue(propertyName).toString()); if (permissionEntList.contains(currentEntId)) { metaObject.setValue(propertyName, currentEntId); } else { throw new UserOverreachException(); } }
} } }
|
关联查询
更新
删除
代码地址
引用站外地址
上文代码在目录cn.allbs.allbsjwt.config.datascope
下,可以查看git提交记录,后面使用封装好的allbs-mybatis包就被我删了。
引用站外地址