基于配置文件实现(关键key存储在配置文件,通过读取配置文件来实现动态拼接sql)
1、创建注解类
@UserDataPermission(id="app")
注:id用以区分是小程序还是应用程序
注解加的位置:
2、配置枚举类配置文件 EDataPermissionType
3、创建拦截器重写InnerInterceptor接口,重写查询方法
/**
* 拦截器
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@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));
}
}
/**
* 设置 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);
}
}
}
4、创建处理类,动态拼接sql片段,设置where
/**
* 拦截器处理器
*/
@Slf4j
public class MyDataPermissionHandler {
private ISysUserDataRelationService sysUserDataRelationService = new SysUserDataRelationServiceImpl();
private MpcTokenUtil mpcTokenUtil = new MpcTokenUtil();
private AppManageConfig appManageConfig;
/**
* 获取数据权限 SQL 片段
*
* @param plainSelect 查询对象
* @param whereSegment 查询条件片段
* @return JSqlParser 条件表达式
*/
@SneakyThrows(Exception.class)
public Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) {
sysUserDataRelationService = SpringUtils.getBean(ISysUserDataRelationService.class);
mpcTokenUtil = SpringUtils.getBean(MpcTokenUtil.class);
appManageConfig = SpringUtils.getBean(AppManageConfig.class);
// 待执行 SQL Where 条件表达式
Expression where = plainSelect.getWhere();
if (where == null) {
where = new HexValue(" 1 = 1 ");
}
//获取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 的所有方法,判断方法上是否有 UserDataPermission
for (Method m : methods) {
if (Objects.equals(m.getName(), methodName)) {
UserDataPermission annotation = m.getAnnotation(UserDataPermission.class);
if (annotation == null) {
return where;
}
String type = annotation.id();
//小程序或应用程序的集合,in的范围
List<String> dataIds = sysUserDataRelationService.getUserPermission(mpcTokenUtil.getUserAccountByToken(), EDataPermissionType.getCode(type));
if (CollectionUtils.isEmpty(dataIds)) {
return null;
}
log.info("开始进行权限过滤,where: {},mappedStatementId: {}", where, whereSegment);
// 把集合转变为JSQLParser需要的元素列表
ItemsList ids = new ExpressionList(dataIds.stream().map(StringValue::new).collect(Collectors.toList()));
//in表达式的写法
InExpression inExpressiondept = null;
String key = appManageConfig.getList().get(type);
inExpressiondept = new InExpression(new Column(mainTableName + "." + key), ids);
return new AndExpression(where, inExpressiondept);
}
}
//说明无权查看,
where = new HexValue(" 1 = 2 ");
return where;
}
}
5、将拦截器加到mybatis-plus插件中
@Configuration
@MapperScan("com.shinho.mpc.mapper")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 将拦截器加到mybatis插件中
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加数据权限插件
MyDataPermissionInterceptor dataPermissionInterceptor = new MyDataPermissionInterceptor();
// 添加自定义的数据权限处理器
dataPermissionInterceptor.setDataPermissionHandler(new MyDataPermissionHandler());
interceptor.addInnerInterceptor(dataPermissionInterceptor);
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
6、使用的位置加注解即可生效
在mapper层加上注解:
@UserDataPermission(id="app")
id :注解入餐
app:应用程序类型权限控制
mp:小程序类型权限控制
7、数据拦截效果: