From aae3fe5305518b5d950bbf40f6c1fea6d76881b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 13 Dec 2021 03:49:05 +0000 Subject: [PATCH] =?UTF-8?q?update=20[=E9=87=8D=E5=A4=A7=E6=9B=B4=E6=96=B0]?= =?UTF-8?q?=20=E9=87=8D=E5=86=99=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/common/annotation/DataColumn.java | 25 ++++ .../common/annotation/DataPermission.java | 17 +++ .../ruoyi/common/annotation/DataScope.java | 4 + .../common/core/domain/entity/SysUser.java | 2 +- .../com/ruoyi/common/enums/DataScopeType.java | 66 +++++++++ .../com/ruoyi/demo/mapper/TestDemoMapper.java | 39 +++++ .../com/ruoyi/demo/mapper/TestTreeMapper.java | 29 ++++ .../service/impl/TestDemoServiceImpl.java | 11 +- .../service/impl/TestTreeServiceImpl.java | 5 - .../PlusDataPermissionInterceptor.java | 96 +++++++++++++ .../framework/aspectj/DataScopeAspect.java | 2 + .../framework/config/MybatisPlusConfig.java | 10 ++ .../handler/PlusDataPermissionHandler.java | 133 ++++++++++++++++++ .../ruoyi/system/mapper/SysDeptMapper.java | 5 + .../ruoyi/system/mapper/SysRoleMapper.java | 8 ++ .../ruoyi/system/mapper/SysUserMapper.java | 18 +++ .../system/service/SysDataScopeService.java | 24 ++++ .../service/impl/SysDataScopeServiceImpl.java | 50 +++++++ .../service/impl/SysDeptServiceImpl.java | 4 +- .../service/impl/SysRoleServiceImpl.java | 3 - .../service/impl/SysUserServiceImpl.java | 5 - .../resources/mapper/system/SysDeptMapper.xml | 8 +- .../resources/mapper/system/SysRoleMapper.xml | 16 +-- .../resources/mapper/system/SysUserMapper.xml | 16 +-- 24 files changed, 551 insertions(+), 45 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java new file mode 100644 index 000000000..936466697 --- /dev/null +++ b/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"; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java new file mode 100644 index 000000000..e04b23696 --- /dev/null +++ b/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(); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java index feaa3f42c..f1a4f9f34 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java +++ b/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; + } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java index 2fa57f5ca..dd23d3e54 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java +++ b/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) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java new file mode 100644 index 000000000..f99413bad --- /dev/null +++ b/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; + } +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java index dccea6154..7a3cb62d6 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java +++ b/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 { + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) Page customPageList(@Param("page") Page page, @Param("ew") Wrapper wrapper); + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) +

> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectList(@Param(Constants.WRAPPER) Wrapper 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 idList); } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java index 05b0153b8..388095009 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java +++ b/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 { + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectList(@Param(Constants.WRAPPER) Wrapper 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 idList); } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java index f717bfffd..1cb526848 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java +++ b/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 queryPageList(TestDemoBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -47,7 +45,6 @@ public class TestDemoServiceImpl extends ServicePlusImpl customPageList(TestDemoBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -55,7 +52,6 @@ public class TestDemoServiceImpl extends ServicePlusImpl queryList(TestDemoBo bo) { return listVo(buildQueryWrapper(bo)); @@ -63,14 +59,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl buildQueryWrapper(TestDemoBo bo) { Map params = bo.getParams(); - Object dataScope = params.get("dataScope"); LambdaQueryWrapper 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; } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java index daff12bd1..c4097e667 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java +++ b/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 queryList(TestTreeBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -42,13 +40,10 @@ public class TestTreeServiceImpl extends ServicePlusImpl buildQueryWrapper(TestTreeBo bo) { Map params = bo.getParams(); - Object dataScope = params.get("dataScope"); LambdaQueryWrapper 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; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 000000000..1c3d6e02a --- /dev/null +++ b/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 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); + } + } + +} + diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index 7392d833e..1a0d8e042 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/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 { /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java index db81b7437..aeae4a31c 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java +++ b/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(); + } + /** * 分页插件,自动识别数据库类型 */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java new file mode 100644 index 000000000..89897c31d --- /dev/null +++ b/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 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; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java index d0ae46dcd..317be7e3c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java +++ b/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 { * @param dept 部门信息 * @return 部门信息集合 */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) List selectDeptList(SysDept dept); /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java index eb440065f..d2721ee4d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java +++ b/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 { + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) Page selectPageRoleList(@Param("page") Page page, @Param("role") SysRole role); /** @@ -22,6 +27,9 @@ public interface SysRoleMapper extends BaseMapperPlus { * @param role 角色信息 * @return 角色数据集合信息 */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) List selectRoleList(SysRole role); /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java index e930d879a..1a07f7f92 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java +++ b/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 { + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) Page selectPageUserList(@Param("page") Page page, @Param("user") SysUser user); /** @@ -22,6 +28,10 @@ public interface SysUserMapper extends BaseMapperPlus { * @param sysUser 用户信息 * @return 用户信息集合信息 */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) List selectUserList(SysUser sysUser); /** @@ -30,6 +40,10 @@ public interface SysUserMapper extends BaseMapperPlus { * @param user 用户信息 * @return 用户信息集合信息 */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) Page selectAllocatedList(@Param("page") Page page, @Param("user") SysUser user); /** @@ -38,6 +52,10 @@ public interface SysUserMapper extends BaseMapperPlus { * @param user 用户信息 * @return 用户信息集合信息 */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) Page selectUnallocatedList(@Param("page") Page page, @Param("user") SysUser user); /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java new file mode 100644 index 000000000..34e2ea33c --- /dev/null +++ b/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); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java new file mode 100644 index 000000000..84399585b --- /dev/null +++ b/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 list = roleDeptMapper.selectList( + new LambdaQueryWrapper() + .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 list = deptMapper.selectList(new LambdaQueryWrapper() + .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; + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java index 8b4c5dbdd..0f8897fe4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java +++ b/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 selectDeptList(SysDept dept) { +// return baseMapper.selectList(); +// return baseMapper.selectList(new LambdaQueryWrapper<>()); return baseMapper.selectDeptList(dept); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java index de47ff0ba..cea08c8e2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java +++ b/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 selectPageRoleList(SysRole role, PageQuery pageQuery) { Page page = baseMapper.selectPageRoleList(PageUtils.buildPage(pageQuery), role); return PageUtils.buildDataInfo(page); @@ -59,7 +57,6 @@ public class SysRoleServiceImpl extends ServicePlusImpl selectRoleList(SysRole role) { return baseMapper.selectRoleList(role); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index be9999424..3af2fa818 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java +++ b/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 selectPageUserList(SysUser user, PageQuery pageQuery) { Page page = baseMapper.selectPageUserList(PageUtils.buildPage(pageQuery), user); return PageUtils.buildDataInfo(page); @@ -67,7 +65,6 @@ public class SysUserServiceImpl extends ServicePlusImpl selectUserList(SysUser user) { return baseMapper.selectUserList(user); } @@ -79,7 +76,6 @@ public class SysUserServiceImpl extends ServicePlusImpl selectAllocatedList(SysUser user, PageQuery pageQuery) { Page page = baseMapper.selectAllocatedList(PageUtils.buildPage(pageQuery), user); return PageUtils.buildDataInfo(page); @@ -92,7 +88,6 @@ public class SysUserServiceImpl extends ServicePlusImpl selectUnallocatedList(SysUser user, PageQuery pageQuery) { Page page = baseMapper.selectUnallocatedList(PageUtils.buildPage(pageQuery), user); return PageUtils.buildDataInfo(page); diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index 346332c76..2c1fc7590 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -42,10 +42,10 @@ AND status = #{status} - - - AND ( ${params.dataScope} ) - + + + + order by d.parent_id, d.order_num diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index 94b161b3b..c13bd68bb 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -60,10 +60,10 @@ and date_format(r.create_time,'%y%m%d') <= date_format(#{role.params.endTime},'%y%m%d') - - - AND ( ${role.params.dataScope} ) - + + + + order by r.role_sort @@ -88,10 +88,10 @@ and date_format(r.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') - - - AND ( ${params.dataScope} ) - + + + + order by r.role_sort diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 5d9a18568..d9a927fb2 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/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) )) - - - AND ( ${user.params.dataScope} ) - + + + +