疯狂的狮子Li
2021-12-13 aae3fe5305518b5d950bbf40f6c1fea6d76881b3
update [重大更新] 重写数据权限实现
已添加7个文件
已修改17个文件
596 ■■■■■ 文件已修改
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusDataPermissionInterceptor.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/SysDataScopeService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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";
}
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();
}
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
@@ -6,11 +6,14 @@
 * æ•°æ®æƒé™è¿‡æ»¤æ³¨è§£
 *
 * @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 @@
     * æ˜¯å¦è¿‡æ»¤ç”¨æˆ·æƒé™
     */
    boolean isUser() default false;
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -162,7 +162,7 @@
    private Long[] postIds;
    /**
     * è§’色ID
     * æ•°æ®æƒé™ å½“前角色ID
     */
    @ApiModelProperty(value = "角色ID")
    @TableField(exist = false)
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;
    }
}
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
@@ -1,11 +1,19 @@
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 @@
 */
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);
}
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 @@
 */
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);
}
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 @@
        return getVoById(id);
    }
    @DataScope(isUser = true)
    @Override
    public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
@@ -47,7 +45,6 @@
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @DataScope(isUser = true)
    @Override
    public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
@@ -55,7 +52,6 @@
        return PageUtils.buildDataInfo(result);
    }
    @DataScope(isUser = true)
    @Override
    public List<TestDemoVo> queryList(TestDemoBo bo) {
        return listVo(buildQueryWrapper(bo));
@@ -63,14 +59,11 @@
    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;
    }
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
@@ -3,7 +3,6 @@
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 @@
    }
//    @DS("slave") // åˆ‡æ¢ä»Žåº“查询
    @DataScope(isUser = true)
    @Override
    public List<TestTreeVo> queryList(TestTreeBo bo) {
        LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
@@ -42,13 +40,10 @@
    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;
    }
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);
        }
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
@@ -18,9 +18,11 @@
 * æ•°æ®è¿‡æ»¤å¤„理
 *
 * @author Lion Li
 * @deprecated 3.6.0 ç§»é™¤ {@link com.ruoyi.framework.handler.PlusDataPermissionHandler}
 */
@Aspect
@Component
@Deprecated
public class DataScopeAspect {
    /**
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
@@ -9,6 +9,7 @@
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 @@
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // æ•°æ®æƒé™å¤„理
        interceptor.addInnerInterceptor(dataPermissionInterceptor());
        // åˆ†é¡µæ’ä»¶
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // ä¹è§‚锁插件
@@ -37,6 +40,13 @@
        return interceptor;
    }
    /**
     * æ•°æ®æƒé™æ‹¦æˆªå™¨
     */
    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
        return new PlusDataPermissionInterceptor();
    }
    /**
     * åˆ†é¡µæ’件,自动识别数据库类型
     */
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;
    }
}
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 @@
     * @param dept éƒ¨é—¨ä¿¡æ¯
     * @return éƒ¨é—¨ä¿¡æ¯é›†åˆ
     */
    @DataPermission({
        @DataColumn(key = "deptName", value = "d.dept_id")
    })
    List<SysDept> selectDeptList(SysDept dept);
    /**
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 @@
 */
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 @@
     * @param role è§’色信息
     * @return è§’色数据集合信息
     */
    @DataPermission({
        @DataColumn(key = "deptName", value = "d.dept_id")
    })
    List<SysRole> selectRoleList(SysRole role);
    /**
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 @@
 */
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 @@
     * @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 @@
     * @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 @@
     * @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);
    /**
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);
}
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;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -5,7 +5,6 @@
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 @@
     * @return éƒ¨é—¨ä¿¡æ¯é›†åˆ
     */
    @Override
    @DataScope(deptAlias = "d")
    public List<SysDept> selectDeptList(SysDept dept) {
//        return baseMapper.selectList();
//        return baseMapper.selectList(new LambdaQueryWrapper<>());
        return baseMapper.selectDeptList(dept);
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -2,7 +2,6 @@
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 @@
    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 @@
     * @return è§’色数据集合信息
     */
    @Override
    @DataScope(deptAlias = "d")
    public List<SysRole> selectRoleList(SysRole role) {
        return baseMapper.selectRoleList(role);
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -4,7 +4,6 @@
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 @@
    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 @@
     * @return ç”¨æˆ·ä¿¡æ¯é›†åˆä¿¡æ¯
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
    public List<SysUser> selectUserList(SysUser user) {
        return baseMapper.selectUserList(user);
    }
@@ -79,7 +76,6 @@
     * @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 @@
     * @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);
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>
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>
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">