数据权限注解@DataScope

849 阅读2分钟

- 1. -关于@DataScope 注解的使用

  1. ** @DataScope**,我们系统设计上经常在做访问权限, 大部分是shiro 或者security等,但是数据权限之前很少用成熟的技术方案;

  2. 现阶段需要使用到 数据权限 结合着 访问权限 来做这个权限控制, 于是想起来了之前看到的一个写法,来源于 “若依”的一个aop注解 @DataScope;

  3. 两种业务逻辑 如果你们是 成熟的产品, 我建议你用第一种设计方案, 如果你们是定制化的产品, 我建议你做第二种方案;

    3.1:成熟的设计方案,在sys_role表中新增data_scope 字段,用来表示当前角色的数据权限范围,

    数据权限范围定义
    //全部
    public static final Integer DATA_SCOPE_ALL = 1;
    //自定义数据
    public static final Integer DATA_SCOPE_CUSTOM = 2;
    //本部门
    public static final Integer DATA_SCOPE_DEPT = 3;
    //本部门及部门一下
    public static final Integer DATA_SCOPE_DEPT_AND_CHILD = 4;
    //个人
    public static final Integer DATA_SCOPE_SELF = 5;
    //权限参数字段
    public static final String DATA_SCOPE = "dataScope";
    

    新增 数据库表字段如下图(图片转载于博主:渣渣成长之路)

image.png

@DataScope(deptAlias = "s",menuId = Constants.MENU_ID_ATTENDANCE_CARD)
public ApiResult query00001(){
 //此处省略业务具体代码
}

自定义注解类


/**
 * 数据权限过滤注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {
    /**
     * 部门表的别名
     */
    public String deptAlias() default "";

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

切面类DataScopeAspect

// 配置织入点
@Pointcut("@annotation(com.tacs.common.annotation.DataScope)")
public void dataScopePointCut() {
}

@Before("dataScopePointCut()")
public void doBefore(JoinPoint point) throws Throwable {
    handleDataScope(point);
}


protected void handleDataScope(final JoinPoint joinPoint) {
    // 获得注解
    DataScope controllerDataScope = getAnnotationLog(joinPoint);
    if (controllerDataScope == null) {
        return;
    }
    // 获取当前的用户
    UserDO user = 自定义获取用户信息方法;
    if (!StringUtils.isEmpty(user)) {
        // 如果是超级管理员,则不过滤数据
        if (!StringUtils.isEmpty(user) && !user.getIsAdmin()) {
            dataScopeFilter(joinPoint, user, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), controllerDataScope.menuId());
        }
    }
}

/**
 * 数据范围过滤
 *
 * @param joinPoint 切点
 * @param user      用户
 * @param userAlias 别名
 */
public void dataScopeFilter(JoinPoint joinPoint, UserDO user, String deptAlias, String userAlias, String menuId) {
    //初始化数据权限参数值
    Integer dataScope = 0;
    //数据权限sql参数
    StringBuilder sqlString = new StringBuilder();
    //  获取当前登陆人所有角色
    QueryWrapper<UserRoleDO> userRoleDOQueryWrapper = new QueryWrapper<>();
    userRoleDOQueryWrapper.eq("user_id", user.getId());
    List<UserRoleDO> userRols = userRoleMapper.selectList(userRoleDOQueryWrapper);
    if (StringUtils.isEmpty(userRols)) {
        log.info("获取当前登陆人角色信息失败,当前人员ID为:{},获取角色数据大小为:{}", user.getId(), userRols.size());
        return;
    }
    QueryWrapper<SysRoleDataScope> sysRoleDataScopeQueryWrapper = new QueryWrapper<>();
     for (SysRole role : user.getRoles())
        {
            String dataScope = role.getDataScope();
            if (DATA_SCOPE_ALL.equals(dataScope))
            {
                sqlString = new StringBuilder();
                break;
            }
            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
                        role.getRoleId()));
            }
            else if (DATA_SCOPE_DEPT.equals(dataScope))
            {
                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
            }
            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                        deptAlias, user.getDeptId(), user.getDeptId()));
            }
            else if (DATA_SCOPE_SELF.equals(dataScope))
            {
                if (StringUtils.isNotBlank(userAlias))
                {
                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                }
                else
                {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(" OR 1=0 ");
                }
            }
        }
    if (!StringUtils.isEmpty(sqlString.toString())) {
        Object params = joinPoint.getArgs()[0];
        if (!StringUtils.isEmpty(params) && params instanceof BasePageQuery) {
            BasePageQuery basePageQuery = (BasePageQuery) params;
            Map<String,Object> param = new HashMap<>();
            param.put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
            basePageQuery.setParams(param);
        }
    }

}

/**
 * 是否存在注解,如果存在就获取
 */
private DataScope getAnnotationLog(JoinPoint joinPoint) {
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();

    if (method != null) {
        return method.getAnnotation(DataScope.class);
    }
    return null;
}

Mapper.xml 通过${params.dataScope}来完成sql的拼接。params是实体类父类中的属性类型,类型为Map。从该属性中可以获取到需要拼接的sql。需要拼接的sql是在切面类中前置方法中存入的。(baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + “)”);)

SELECT
  *
from
  具体业务表
<!-- 数据范围过滤 -->
${params.dataScope}

order BY s.create_time  DESC

注: 代码参考与博主 渣渣成长之路