浏览代码

update [重大更新] 重写数据权限实现

疯狂的狮子Li 3 年之前
父节点
当前提交
aae3fe5305
共有 24 个文件被更改,包括 551 次插入45 次删除
  1. 25 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java
  2. 17 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java
  3. 4 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
  4. 1 1
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
  5. 66 0
      ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java
  6. 39 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
  7. 29 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
  8. 2 9
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
  9. 0 5
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
  10. 96 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java
  11. 2 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
  12. 10 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
  13. 133 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
  14. 5 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
  15. 8 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
  16. 18 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
  17. 24 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java
  18. 50 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java
  19. 2 2
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
  20. 0 3
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
  21. 0 5
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
  22. 4 4
      ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
  23. 8 8
      ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
  24. 8 8
      ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

+ 25 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java

@@ -0,0 +1,25 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限
+ *
+ * @author Lion Li
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataColumn {
+
+    /**
+     * 占位符关键字
+     */
+    String key() default "deptName";
+
+    /**
+     * 占位符替换值
+     */
+    String value() default "dept_id";
+
+}

+ 17 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java

@@ -0,0 +1,17 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限组
+ *
+ * @author Lion Li
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataPermission {
+
+    DataColumn[] value();
+
+}

+ 4 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java

@@ -6,11 +6,14 @@ import java.lang.annotation.*;
  * 数据权限过滤注解
  *
  * @author ruoyi
+ * @deprecated 3.6.0 移除 {@link com.ruoyi.common.annotation.DataPermission}
  */
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
+@Deprecated
 public @interface DataScope {
+
     /**
      * 部门表的别名
      */
@@ -25,4 +28,5 @@ public @interface DataScope {
      * 是否过滤用户权限
      */
     boolean isUser() default false;
+
 }

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

@@ -162,7 +162,7 @@ public class SysUser extends BaseEntity {
 	private Long[] postIds;
 
 	/**
-	 * 角色ID
+	 * 数据权限 当前角色ID
 	 */
 	@ApiModelProperty(value = "角色ID")
 	@TableField(exist = false)

+ 66 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java

@@ -0,0 +1,66 @@
+package com.ruoyi.common.enums;
+
+import com.ruoyi.common.utils.StringUtils;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 数据权限类型
+ *
+ * 语法支持 spel 模板表达式
+ *
+ * 内置数据 user 当前用户 内容参考 SysUser
+ * 如需扩展数据 需往 SysUser 内注入
+ * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService
+ * 如需扩展更多自定义服务 可以参考 sdss 自行编写
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum DataScopeType {
+
+    /**
+     * 全部数据权限
+     */
+    ALL("1", ""),
+
+    /**
+     * 自定数据权限
+     */
+    CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) "),
+
+    /**
+     * 部门数据权限
+     */
+    DEPT("3", " #{#deptName} = #{#user.deptId} "),
+
+    /**
+     * 部门及以下数据权限
+     */
+    DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )"),
+
+    /**
+     * 仅本人数据权限
+     */
+    SELF("5", " #{#userName?:1} = #{#user.userId} ");
+
+    private final String code;
+
+    /**
+     * 语法 采用 spel 模板表达式
+     */
+    private final String sql;
+
+    public static DataScopeType findCode(String code) {
+        if (StringUtils.isBlank(code)) {
+            return null;
+        }
+        for (DataScopeType type : values()) {
+            if (type.getCode().equals(code)) {
+                return type;
+            }
+        }
+        return null;
+    }
+}

+ 39 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java

@@ -1,12 +1,20 @@
 package com.ruoyi.demo.mapper;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
 import org.apache.ibatis.annotations.Param;
 
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
 /**
  * 测试单表Mapper接口
  *
@@ -15,6 +23,37 @@ import org.apache.ibatis.annotations.Param;
  */
 public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
 
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
     Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
 
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    <P extends IPage<TestDemo>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    List<TestDemo> selectList(@Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int updateById(@Param(Constants.ENTITY) TestDemo entity);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
 }

+ 29 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java

@@ -1,7 +1,16 @@
 package com.ruoyi.demo.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.demo.domain.TestTree;
+import org.apache.ibatis.annotations.Param;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
 
 /**
  * 测试树表Mapper接口
@@ -11,4 +20,24 @@ import com.ruoyi.demo.domain.TestTree;
  */
 public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
 
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    List<TestTree> selectList(@Param(Constants.WRAPPER) Wrapper<TestTree> queryWrapper);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int updateById(@Param(Constants.ENTITY) TestTree entity);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
 }

+ 2 - 9
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java

@@ -1,16 +1,15 @@
 package com.ruoyi.demo.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import com.ruoyi.common.core.domain.PageQuery;
-import com.ruoyi.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
 import com.ruoyi.common.core.page.PagePlus;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.bo.TestDemoBo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
@@ -36,7 +35,6 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 		return getVoById(id);
 	}
 
-	@DataScope(isUser = true)
 	@Override
 	public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
@@ -47,7 +45,6 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 	/**
 	 * 自定义分页查询
 	 */
-	@DataScope(isUser = true)
 	@Override
 	public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
@@ -55,7 +52,6 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 		return PageUtils.buildDataInfo(result);
 	}
 
-	@DataScope(isUser = true)
 	@Override
 	public List<TestDemoVo> queryList(TestDemoBo bo) {
 		return listVo(buildQueryWrapper(bo));
@@ -63,14 +59,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 
 	private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
 		Map<String, Object> params = bo.getParams();
-		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
 		lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
 		lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
-			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}
 

+ 0 - 5
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java

@@ -3,7 +3,6 @@ package com.ruoyi.demo.service.impl;
 import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.demo.domain.TestTree;
@@ -33,7 +32,6 @@ public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTre
 	}
 
 //	@DS("slave") // 切换从库查询
-    @DataScope(isUser = true)
 	@Override
 	public List<TestTreeVo> queryList(TestTreeBo bo) {
         LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
@@ -42,13 +40,10 @@ public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTre
 
 	private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
 		Map<String, Object> params = bo.getParams();
-		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
 		lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
-			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}
 

+ 96 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java

@@ -0,0 +1,96 @@
+package com.ruoyi.framework.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 com.ruoyi.framework.handler.PlusDataPermissionHandler;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.statement.delete.Delete;
+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.Executor;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
+
+    private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
+
+    @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(parserSingle(mpBs.sql(), ms.getId()));
+    }
+
+    @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) {
+            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 processUpdate(Update update, int index, String sql, Object obj) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);
+        if (null != sqlSegment) {
+            update.setWhere(sqlSegment);
+        }
+    }
+
+    @Override
+    protected void processDelete(Delete delete, int index, String sql, Object obj) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);
+        if (null != sqlSegment) {
+            delete.setWhere(sqlSegment);
+        }
+    }
+
+    /**
+     * 设置 where 条件
+     *
+     * @param plainSelect       查询对象
+     * @param mappedStatementId 执行方法id
+     */
+    protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);
+        if (null != sqlSegment) {
+            plainSelect.setWhere(sqlSegment);
+        }
+    }
+
+}
+

+ 2 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java

@@ -18,9 +18,11 @@ import org.springframework.stereotype.Component;
  * 数据过滤处理
  *
  * @author Lion Li
+ * @deprecated 3.6.0 移除 {@link com.ruoyi.framework.handler.PlusDataPermissionHandler}
  */
 @Aspect
 @Component
+@Deprecated
 public class DataScopeAspect {
 
 	/**

+ 10 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import com.ruoyi.common.core.mybatisplus.methods.InsertAll;
+import com.ruoyi.framework.Interceptor.PlusDataPermissionInterceptor;
 import com.ruoyi.framework.handler.CreateAndUpdateMetaObjectHandler;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.context.annotation.Bean;
@@ -30,6 +31,8 @@ public class MybatisPlusConfig {
 	@Bean
 	public MybatisPlusInterceptor mybatisPlusInterceptor() {
 		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 数据权限处理
+        interceptor.addInnerInterceptor(dataPermissionInterceptor());
 		// 分页插件
 		interceptor.addInnerInterceptor(paginationInnerInterceptor());
 		// 乐观锁插件
@@ -37,6 +40,13 @@ public class MybatisPlusConfig {
 		return interceptor;
 	}
 
+    /**
+     * 数据权限拦截器
+     */
+    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
+        return new PlusDataPermissionInterceptor();
+    }
+
 	/**
 	 * 分页插件,自动识别数据库类型
 	 */

+ 133 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java

@@ -0,0 +1,133 @@
+package com.ruoyi.framework.handler;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.service.UserService;
+import com.ruoyi.common.enums.DataScopeType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.expression.BeanResolver;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.ParserContext;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 数据权限过滤
+ *
+ * @author Lion Li
+ */
+@Slf4j
+public class PlusDataPermissionHandler {
+
+    private final ExpressionParser parser = new SpelExpressionParser();
+    private final ParserContext parserContext = new TemplateParserContext();
+    private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
+
+    public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
+        DataColumn[] dataColumns = findAnnotation(mappedStatementId);
+        if (ArrayUtil.isEmpty(dataColumns)) {
+            return where;
+        }
+        SysUser currentUser = SpringUtils.getBean(UserService.class).selectUserById(SecurityUtils.getUserId());
+        // 如果是超级管理员,则不过滤数据
+        if (StringUtils.isNull(currentUser) || currentUser.isAdmin()) {
+            return where;
+        }
+        String dataFilterSql = buildDataFilter(currentUser, dataColumns, isSelect);
+        if (StringUtils.isBlank(dataFilterSql)) {
+            return where;
+        }
+        try {
+            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
+            if (ObjectUtil.isNotNull(where)) {
+                return new AndExpression(where, expression);
+            } else {
+                return expression;
+            }
+        } catch (JSQLParserException e) {
+            throw new ServiceException("数据权限解析异常 => " + e.getMessage());
+        }
+    }
+
+    /**
+     * 构造数据过滤sql
+     */
+    private String buildDataFilter(SysUser user, DataColumn[] dataColumns, boolean isSelect) {
+        StringBuilder sqlString = new StringBuilder();
+
+        StandardEvaluationContext context = new StandardEvaluationContext();
+        context.setBeanResolver(beanResolver);
+        context.setVariable("user", user);
+
+        for (DataColumn dataColumn : dataColumns) {
+            // 设置注解变量 key 为表达式变量 value 为变量值
+            context.setVariable(dataColumn.key(), dataColumn.value());
+            for (SysRole role : user.getRoles()) {
+                user.setRoleId(role.getRoleId());
+
+                // 获取角色权限泛型
+                DataScopeType type = DataScopeType.findCode(role.getDataScope());
+                if (ObjectUtil.isNull(type)) {
+                    throw new ServiceException("角色数据范围异常 => " + role.getDataScope());
+                }
+                // 全部数据权限直接返回
+                if (type == DataScopeType.ALL) {
+                    return "";
+                }
+                // 不包含 key 变量 则不处理
+                if (!StringUtils.contains(type.getSql(), "#" + dataColumn.key())) {
+                    continue;
+                }
+                // 更新或删除需满足所有条件
+                sqlString.append(isSelect ? " OR " : " AND ");
+                // 解析sql模板并填充
+                String sql = parser.parseExpression(type.getSql(), parserContext).getValue(context, String.class);
+                sqlString.append(sql);
+            }
+        }
+
+        if (StringUtils.isNotBlank(sqlString.toString())) {
+            return sqlString.substring(isSelect ? 4 : 5);
+        }
+        return "";
+    }
+
+    private DataColumn[] findAnnotation(String mappedStatementId) {
+        StringBuilder sb = new StringBuilder(mappedStatementId);
+        int index = sb.lastIndexOf(".");
+        String clazzName = sb.substring(0, index);
+        String methodName = sb.substring(index + 1, sb.length());
+        Class<?> clazz = ClassUtil.loadClass(clazzName);
+        List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
+            .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
+        DataPermission dataPermission;
+        for (Method method : methods) {
+            if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
+                dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
+                return dataPermission.value();
+            }
+        }
+        return null;
+    }
+}

+ 5 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java

@@ -1,5 +1,7 @@
 package com.ruoyi.system.mapper;
 
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import org.apache.ibatis.annotations.Param;
@@ -19,6 +21,9 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept> {
      * @param dept 部门信息
      * @return 部门信息集合
      */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id")
+    })
     List<SysDept> selectDeptList(SysDept dept);
 
     /**

+ 8 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java

@@ -1,6 +1,8 @@
 package com.ruoyi.system.mapper;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import org.apache.ibatis.annotations.Param;
@@ -14,6 +16,9 @@ import java.util.List;
  */
 public interface SysRoleMapper extends BaseMapperPlus<SysRole> {
 
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id")
+    })
     Page<SysRole> selectPageRoleList(@Param("page") Page<SysRole> page, @Param("role") SysRole role);
 
     /**
@@ -22,6 +27,9 @@ public interface SysRoleMapper extends BaseMapperPlus<SysRole> {
      * @param role 角色信息
      * @return 角色数据集合信息
      */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id")
+    })
     List<SysRole> selectRoleList(SysRole role);
 
     /**

+ 18 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -1,6 +1,8 @@
 package com.ruoyi.system.mapper;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import org.apache.ibatis.annotations.Param;
@@ -14,6 +16,10 @@ import java.util.List;
  */
 public interface SysUserMapper extends BaseMapperPlus<SysUser> {
 
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id"),
+        @DataColumn(key = "userName", value = "u.user_id")
+    })
     Page<SysUser> selectPageUserList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
 
     /**
@@ -22,6 +28,10 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser> {
      * @param sysUser 用户信息
      * @return 用户信息集合信息
      */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id"),
+        @DataColumn(key = "userName", value = "u.user_id")
+    })
     List<SysUser> selectUserList(SysUser sysUser);
 
     /**
@@ -30,6 +40,10 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser> {
      * @param user 用户信息
      * @return 用户信息集合信息
      */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id"),
+        @DataColumn(key = "userName", value = "u.user_id")
+    })
     Page<SysUser> selectAllocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
 
     /**
@@ -38,6 +52,10 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser> {
      * @param user 用户信息
      * @return 用户信息集合信息
      */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "d.dept_id"),
+        @DataColumn(key = "userName", value = "u.user_id")
+    })
     Page<SysUser> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
 
     /**

+ 24 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java

@@ -0,0 +1,24 @@
+package com.ruoyi.system.service;
+
+/**
+ * 通用 数据权限 服务
+ *
+ * @author Lion Li
+ */
+public interface SysDataScopeService {
+
+    /**
+     * 获取角色自定义权限
+     * @param roleId 角色id
+     * @return 部门id组
+     */
+    String getRoleCustom(Long roleId);
+
+    /**
+     * 获取部门及以下权限
+     * @param deptId 部门id
+     * @return 部门id组
+     */
+    String getDeptAndChild(Long deptId);
+
+}

+ 50 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java

@@ -0,0 +1,50 @@
+package com.ruoyi.system.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.system.domain.SysRoleDept;
+import com.ruoyi.system.mapper.SysDeptMapper;
+import com.ruoyi.system.mapper.SysRoleDeptMapper;
+import com.ruoyi.system.service.SysDataScopeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service("sdss")
+public class SysDataScopeServiceImpl implements SysDataScopeService {
+
+    @Autowired
+    private SysRoleDeptMapper roleDeptMapper;
+    @Autowired
+    private SysDeptMapper deptMapper;
+
+    @Override
+    public String getRoleCustom(Long roleId) {
+        List<SysRoleDept> list = roleDeptMapper.selectList(
+            new LambdaQueryWrapper<SysRoleDept>()
+                .select(SysRoleDept::getDeptId)
+                .eq(SysRoleDept::getRoleId, roleId));
+        if (CollUtil.isNotEmpty(list)) {
+            return list.stream().map(rd -> Convert.toStr(rd.getDeptId())).collect(Collectors.joining(","));
+        }
+        return null;
+    }
+
+    @Override
+    public String getDeptAndChild(Long deptId) {
+        List<SysDept> list = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
+            .select(SysDept::getDeptId)
+            .eq(SysDept::getDeptId, deptId)
+            .or()
+            .apply("find_in_set({0},ancestors)", deptId));
+        if (CollUtil.isNotEmpty(list)) {
+            return list.stream().map(d -> Convert.toStr(d.getDeptId())).collect(Collectors.joining(","));
+        }
+        return null;
+    }
+
+}

+ 2 - 2
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java

@@ -5,7 +5,6 @@ import cn.hutool.core.convert.Convert;
 import cn.hutool.core.lang.tree.Tree;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.domain.entity.SysRole;
@@ -47,8 +46,9 @@ public class SysDeptServiceImpl extends ServicePlusImpl<SysDeptMapper, SysDept,
      * @return 部门信息集合
      */
     @Override
-    @DataScope(deptAlias = "d")
     public List<SysDept> selectDeptList(SysDept dept) {
+//        return baseMapper.selectList();
+//        return baseMapper.selectList(new LambdaQueryWrapper<>());
         return baseMapper.selectDeptList(dept);
     }
 

+ 0 - 3
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java

@@ -2,7 +2,6 @@ package com.ruoyi.system.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysRole;
@@ -46,7 +45,6 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole,
     private SysRoleDeptMapper roleDeptMapper;
 
     @Override
-    @DataScope(deptAlias = "d")
     public TableDataInfo<SysRole> selectPageRoleList(SysRole role, PageQuery pageQuery) {
         Page<SysRole> page = baseMapper.selectPageRoleList(PageUtils.buildPage(pageQuery), role);
         return PageUtils.buildDataInfo(page);
@@ -59,7 +57,6 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole,
      * @return 角色数据集合信息
      */
     @Override
-    @DataScope(deptAlias = "d")
     public List<SysRole> selectRoleList(SysRole role) {
         return baseMapper.selectRoleList(role);
     }

+ 0 - 5
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysRole;
@@ -54,7 +53,6 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser,
     private SysUserPostMapper userPostMapper;
 
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public TableDataInfo<SysUser> selectPageUserList(SysUser user, PageQuery pageQuery) {
         Page<SysUser> page = baseMapper.selectPageUserList(PageUtils.buildPage(pageQuery), user);
         return PageUtils.buildDataInfo(page);
@@ -67,7 +65,6 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser,
      * @return 用户信息集合信息
      */
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public List<SysUser> selectUserList(SysUser user) {
         return baseMapper.selectUserList(user);
     }
@@ -79,7 +76,6 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser,
      * @return 用户信息集合信息
      */
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public TableDataInfo<SysUser> selectAllocatedList(SysUser user, PageQuery pageQuery) {
         Page<SysUser> page = baseMapper.selectAllocatedList(PageUtils.buildPage(pageQuery), user);
         return PageUtils.buildDataInfo(page);
@@ -92,7 +88,6 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser,
      * @return 用户信息集合信息
      */
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public TableDataInfo<SysUser> selectUnallocatedList(SysUser user, PageQuery pageQuery) {
         Page<SysUser> page = baseMapper.selectUnallocatedList(PageUtils.buildPage(pageQuery), user);
         return PageUtils.buildDataInfo(page);

+ 4 - 4
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml

@@ -42,10 +42,10 @@
         <if test="status != null and status != ''">
             AND status = #{status}
         </if>
-        <!-- 数据范围过滤 -->
-        <if test="params.dataScope != null and params.dataScope != ''">
-            AND ( ${params.dataScope} )
-        </if>
+<!--        &lt;!&ndash; 数据范围过滤 &ndash;&gt;-->
+<!--        <if test="params.dataScope != null and params.dataScope != ''">-->
+<!--            AND ( ${params.dataScope} )-->
+<!--        </if>-->
         order by d.parent_id, d.order_num
     </select>
 

+ 8 - 8
ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml

@@ -60,10 +60,10 @@
         <if test="role.params.endTime != null and role.params.endTime != ''"><!-- 结束时间检索 -->
             and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{role.params.endTime},'%y%m%d')
         </if>
-        <!-- 数据范围过滤 -->
-        <if test="role.params.dataScope != null and role.params.dataScope != ''">
-            AND ( ${role.params.dataScope} )
-        </if>
+<!--        &lt;!&ndash; 数据范围过滤 &ndash;&gt;-->
+<!--        <if test="role.params.dataScope != null and role.params.dataScope != ''">-->
+<!--            AND ( ${role.params.dataScope} )-->
+<!--        </if>-->
         order by r.role_sort
     </select>
 
@@ -88,10 +88,10 @@
         <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
             and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
         </if>
-        <!-- 数据范围过滤 -->
-        <if test="params.dataScope != null and params.dataScope != ''">
-            AND ( ${params.dataScope} )
-        </if>
+<!--        &lt;!&ndash; 数据范围过滤 &ndash;&gt;-->
+<!--        <if test="params.dataScope != null and params.dataScope != ''">-->
+<!--            AND ( ${params.dataScope} )-->
+<!--        </if>-->
         order by r.role_sort
     </select>
 

+ 8 - 8
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -108,10 +108,10 @@
             AND (u.dept_id = #{user.deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{user.deptId},
             ancestors) ))
         </if>
-        <!-- 数据范围过滤 -->
-        <if test="user.params.dataScope != null and user.params.dataScope != ''">
-            AND ( ${user.params.dataScope} )
-        </if>
+<!--        &lt;!&ndash; 数据范围过滤 &ndash;&gt;-->
+<!--        <if test="user.params.dataScope != null and user.params.dataScope != ''">-->
+<!--            AND ( ${user.params.dataScope} )-->
+<!--        </if>-->
     </select>
 
     <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
@@ -142,10 +142,10 @@
             AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId},
             ancestors) ))
         </if>
-        <!-- 数据范围过滤 -->
-        <if test="params.dataScope != null and params.dataScope != ''">
-            AND ( ${params.dataScope} )
-        </if>
+<!--        &lt;!&ndash; 数据范围过滤 &ndash;&gt;-->
+<!--        <if test="params.dataScope != null and params.dataScope != ''">-->
+<!--            AND ( ${params.dataScope} )-->
+<!--        </if>-->
     </select>
 
     <select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">