ruoyi数据权限改造,控制多个数据

338 阅读4分钟
  • 这里用区域和产品类型来控制工单记录,有区域权限和产品权限才可以查看某一工单记录
  • 2024-09-05更新 修改为双重控制而不是某一个权限就能看
  • 2024-09-20更新 修改bug-多类型并行问题
  • 2024-10-18更新 修改bug-用户只有后面的单角色限制不住数据

package cn.risesun.common.aspectj;

import cn.risesun.common.annotation.DataScope;
import cn.risesun.common.utils.StringUtilsUs;
import cn.risesun.common.domain.BaseEntity;
import cn.risesun.common.domain.entity.SysRole;
import cn.risesun.common.domain.entity.SysUser;
import cn.risesun.common.domain.model.LoginUser;
import cn.risesun.common.text.Convert;
import cn.risesun.common.utils.SecurityUtils;
import cn.risesun.common.security.context.PermissionContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 数据过滤处理
 *
 * @author xinhao
 */
@Aspect
@Component
public class DataScopeAspect {
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";
    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    /**
     * 本级数据权限
     */
    public static final String DATA_SCOPE_THIS = "3";
    /**
     * 本级及以下数据权限
     */
    public static final String DATA_SCOPE_THIS_AND_CHILD = "4";
    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";
    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    public static final String AREA = "Area";
    public static final String PRODUCT_TYPE = "ProductType";

    @Before("@annotation(controllerDataScope)")
    public void doBefore(JoinPoint point, DataScope controllerDataScope) {
        clearDataScope(point);
        handleDataScope(point, controllerDataScope);
    }

    protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) {
        // 获取当前的用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (StringUtilsUs.isNotNull(loginUser)) {
            SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员,则不过滤数据
            if (StringUtilsUs.isNotNull(currentUser) && !currentUser.isAdmin()) {
                String permission = StringUtilsUs.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.productTypeAlias(), controllerDataScope.areaAlias(),
                        controllerDataScope.userAlias(), permission);
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint  切点
     * @param user       用户
     * @param userAlias  用户别名
     * @param permission 权限字符
     */
    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String productTypeAlias, String areaAlias, String userAlias, String permission) {
        ArrayList<String> productTypeCustomIds = new ArrayList<>();
        ArrayList<String> areaCustomIds = new ArrayList<>();
        List<String> conditions = new ArrayList<>();
        List<String> conditions4area = new ArrayList<>();
        StringBuilder sqlString = new StringBuilder();
        StringBuilder sqlString4area = new StringBuilder();
        StringBuilder sqlStringAll = new StringBuilder();
        Set<String> allRolesPermissions = new HashSet<>();
        boolean hasPermission = false;
        user.getRoles().forEach(role -> allRolesPermissions.addAll(role.getPermissions()));

        if (StringUtilsUs.isEmpty(permission) || StringUtilsUs.containsAny(allRolesPermissions, Convert.toStrArray(permission))) {
            hasPermission = true;
        }

        boolean finalHasPermission = hasPermission;
        user.getRoles().forEach(role -> {
            if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && PRODUCT_TYPE.equals(role.getScopeAbout())
                    && finalHasPermission) {
                productTypeCustomIds.add(Convert.toStr(role.getRoleId()));
            }
            if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && AREA.equals(role.getScopeAbout())
                    && finalHasPermission) {
                areaCustomIds.add(Convert.toStr(role.getRoleId()));
            }
        });

        // 菜单权限赋予角色,先比对菜单权限,后控制数据权限
if (hasPermission) {
    // 产品类权限
    for (SysRole role : user.getRoles()) {
        String dataScope = role.getDataScope();
        if (PRODUCT_TYPE.equals(role.getScopeAbout())) {
            // 重复被跳过
            if (conditions.contains(dataScope)) {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                sqlString.append(StringUtilsUs.format(" OR 1=1 "));
                conditions.add(dataScope);
                break;
            } else if (StringUtilsUs.isNotBlank(productTypeAlias)) {
                appendCustomScopeSQL(dataScope, sqlString,
                        productTypeAlias, "productTypeId", user.getProductTypeId(), PRODUCT_TYPE, productTypeCustomIds, role.getRoleId());
                conditions.add(dataScope);
            }
        }
    }
    for (SysRole role : user.getRoles()) {
        String dataScope = role.getDataScope();
        if (AREA.equals(role.getScopeAbout())) {
            if (conditions4area.contains(dataScope)) {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                sqlString4area.append(StringUtilsUs.format(" OR  1=1 "));
                conditions4area.add(dataScope);
                break;
            } else if (StringUtilsUs.isNotBlank(areaAlias)) {
                appendCustomScopeSQL(dataScope, sqlString4area, areaAlias, "areaId", user.getAreaId(), AREA, areaCustomIds, role.getRoleId());
                conditions4area.add(dataScope);
            }
        }
    }
    sqlString.append(StringUtilsUs.format(" OR {}.userId = {} ", userAlias, user.getUserId()));
}


        // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
        if (StringUtilsUs.isEmpty(conditions4area) && StringUtilsUs.isEmpty(conditions)) {
            sqlString.append(StringUtilsUs.format(" OR {}.userId = 0 ", userAlias));
        }
// 2024-10-17修改开始
        Object params = joinPoint.getArgs()[0];
        if (StringUtilsUs.isNotNull(params) && params instanceof BaseEntity) {
            if (StringUtilsUs.isNotBlank(sqlString.toString())) {
                sqlStringAll.append(" AND (" + sqlString.substring(4) + ")");
            }
            if (StringUtilsUs.isNotBlank(sqlString4area.toString())) {
                sqlStringAll.append(" AND (" + sqlString4area.substring(4) + ")");
            }
            BaseEntity baseEntity = (BaseEntity) params;
            baseEntity.getParams().put(DATA_SCOPE, sqlStringAll);
        }
// 2024-10-17修改结束
    }

    private static void appendCustomScopeSQL(String dataScope, StringBuilder sqlString, String alias, String idName, Integer idValue, String roleTable, List<String> customIds, Integer roleId) {
        if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
            if (customIds.size() > 1) {
                sqlString.append(StringUtilsUs.format(
                        " OR {}.{} IN ( SELECT {} FROM Role{} WHERE roleId in ({}) ) ", alias, idName, idName, roleTable, String.join(",", customIds)));
            } else {
                sqlString.append(StringUtilsUs.format(
                        " OR {}.{} IN ( SELECT {} FROM Role{} WHERE roleId = {} ) ", alias, idName, idName, roleTable, roleId));
            }
        } else if (DATA_SCOPE_THIS.equals(dataScope)) {
            sqlString.append(StringUtilsUs.format(" OR {}.{} = {} ", alias, idName, idValue));
        } else if (DATA_SCOPE_THIS_AND_CHILD.equals(dataScope)) {
            sqlString.append(StringUtilsUs.format(
                    " OR {}.{} IN ( SELECT {} FROM {} WHERE {} = {} or CHARINDEX(',' +  '{}' + ',' , ',' +  ancestorIds + ',' ) > 0 )",
                    alias, idName, idName, roleTable, idName, idValue, idValue));
        } else {
            // 数据权限为仅本人
            sqlString.append(StringUtilsUs.format(" OR {}.{} = 0 ", alias, idName));
        }
    }

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        Object params = joinPoint.getArgs()[0];
        if (StringUtilsUs.isNotNull(params) && params instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) params;
            baseEntity.getParams().put(DATA_SCOPE, "");
        }
    }
}
package cn.risesun.common.annotation;

import java.lang.annotation.*;

/**
 * @author xinhao
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
    /**
     * 产品类型表的别名
     */
    public String productTypeAlias() default "";
    /**
     * 区域表的别名
     */
    public String areaAlias() default "";

    /**
     * 用户表的别名
     */
    public String userAlias() default "u";

    /**
     * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
     */
    public String permission() default "";
}
@Data
public class SysRole extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 角色ID */
    @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
    private Integer roleId;

    /** 角色名称 */
    @Excel(name = "角色名称")
    @NotBlank(message = "角色名称不能为空")
    private String roleName;

    /** 角色权限 */
    @Excel(name = "权限字符")
    @NotBlank(message = "权限字符不能为空")
    private String roleKey;

    /** 角色排序 */
    @Excel(name = "角色排序")
    private Integer roleSort;

    /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本级数据权限;4:本级及以下数据权限;5:仅本人数据权限) */
    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本级数据权限,4=本级及以下数据权限,5=仅本人数据权限")
    private String dataScope;

    /** 控制的数据表 */
    private String scopeAbout;

    /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
    private boolean menuCheckStrictly;

    /** 数据树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
    private boolean dataCheckStrictly;

    /** 角色状态(0正常 1停用) */
    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
    private String status;

    /** 删除标志(0代表存在 2代表删除) */
    private String delFlag;

    /** 用户是否存在此角色标识 默认不存在 */
    private boolean flag = false;

    /** 菜单组 */
    private Integer[] menuIds;

    /** 区域组(数据权限) */
    private Integer[] dataIds;

    /** 角色菜单权限 */
    private Set<String> permissions;