关于数据权限的一些思考和解决方法

    最近在研究数据权限的时候,自己做了些思考和实践,本文记录下过程以免忘记。

    一、角色都会配置功能权限,此项内容不做赘述。我在角色表中新增字段data_scope,这个用于标记角色拥有的数据权限,

主要分为3种类型:1代表本人,2代表本部门,3代表本机构。数据权限类型可以自行扩展。表结构如下:

   二、用户登录时查询拥有的角色,从角色中获取data_scope值,排序,取出优先级(类型值最大的)最大的,我设计的是值越大拥有的数据权限越大。

    /**
     * 获取数据权限配置
     *
     * @param userName
     * @return
     */
    @Override
    public Integer getDataScopeByUserName(String userName) {
        Integer dataScope = null;
        //获取角色
        ServiceResult<List<RoleAO>> rolesResult = getRolesByUserName(userName);
        if (rolesResult != null && rolesResult.isSucceed() && !CollectionUtils.isEmpty(rolesResult.getData())) {
            List<Integer> dataScopes = new ArrayList<>();
            for (RoleAO role : rolesResult.getData()) {
                if (role.getDataScope() != null) {
                    dataScopes.add(role.getDataScope());
                }
            }
            if (!CollectionUtils.isEmpty(dataScopes)) {
                dataScope = Collections.max(dataScopes);
            }
        }
        return dataScope;
    }

   三、根据数据权限类型生成数据过滤条件:

    /**
     * 生成数据过滤条件
     *
     * @param dataScope
     * @return
     */
    @Override
    public DataScopeFilter generateDataScopeFilter(UserAO user, Integer dataScope) {
        if (user != null && user.isSuperAdmin()) {
            return null;
        }
        DataScopeFilter filter = new DataScopeFilter();
        StringBuilder sqlFliter = new StringBuilder();
        //没有配置数据权限默认只能查自己的
        if (dataScope == null) {
            sqlFliter.append("u.id = ").append("'").append(user.getId()).append("'");
        } else {
            switch (DataScopeType.getByCode(dataScope)) {
                case SELF:
                    sqlFliter.append("u.id = ").append("'").append(user.getId()).append("'");
                    break;
                case DEPARTMENT:
                    sqlFliter.append("u.agency_code = ").append("'").append(user.getAgencyCode()).append("'");
                    break;
                case ORGANIZATION:
                    //级联获取上下级组织编码
                    List<String> agencyCodes = agencyService.listParentChildCodesByCode(agencyService
                            .listRoot().get(0).getCode());
                    if (!CollectionUtils.isEmpty(agencyCodes)) {
                        agencyCodes = agencyCodes.stream().map(agecyCode -> {
                            return "'" + agecyCode + "'";
                        }).collect(Collectors.toList());
                        String agencyCodeStr = StringUtils.join(agencyCodes, ",");
                        sqlFliter.append("u.agency_code in ").append("(").append(agencyCodeStr).append(")");
                    }
                    break;
            }
        }
        filter.setCondition(sqlFliter.toString());
        return filter;
    }

说明:u.id表示用户id,u代表用户表(很重要,我用的是mybatis,用户表为t_sys_user,agency_code存在于该表中,取别名的时候就取为“u”,方便传入过滤条件,不会产生多个表多有id字段,sql无法区分的问题。),u.agency_code表示用户关联的组织机构,listParentChildCodesByCode方法用于把组织机构根节点和所有子节点节点查出来获取组织结构代码,这里大家其实查询所有组织机构就可以了。如果用户有本机构的权限就拼接成sql包含in关系,即 u.agency_code in (......)。这里这么做主是区分同级别多组织结构的问题。找出符合条件的组织机构传入即可。如果是没有同级别那么其实可以不设置过滤条件,查所有也是可以的。

数据权限类型枚举类:

/**
 * @author yangfeng
 * 数据权限类型
 */
public enum DataScopeType {

    SELF(1, "本人"), DEPARTMENT(2, "本部门"),
    ORGANIZATION(3, "本机构");

    private Integer code;

    private String value;

    DataScopeType(Integer code, String value) {
        this.code = code;
        this.value = value;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }


    public static DataScopeType getByCode(Integer code) {
        for (DataScopeType type : values()) {
            if (type.getCode().equals(code)) {
                return type;
            }
        }
        return null;
    }
}

过滤类:

/**
 * 数据权限过滤
 */
public class DataScopeFilter {

  private String condition;

  public String getCondition() {
    return condition;
  }

  public void setCondition(String condition) {
    this.condition = condition;
  }
}

四、将数据权限集成到具体的业务中:

   /**
     * 查询
     *
     * @return
     */
    @Override
    public ServiceResult<List<WorkLogAO>> list(WorkLogRequest request) {
        request.setDataScopeFilter(AuthUtil.getCurrentUser().getDataScopeFilter());
        ServiceResult<List<WorkLogAO>> ret = new ServiceResult<>();
        PageHelper.startPage(request.getPageNo(), request.getPageSize());
        List<WorkLogAO> workLogAOList = workLogCustomizedMapper.listByCondition(request);
        ret.setData(workLogAOList);
        ret.setSucceed(true);
        ret.setAdditionalProperties("page", Page.obtainPage(new PageInfo<>(workLogAOList)));
        return ret;
    }
/**
 * @author yangfeng
 * @date 2020/04/08
 * @description 工作日志请求
 */
public class WorkLogRequest extends BaseRequest {

    /**
     * 用户名
     */
    private String userName;

    /**
     * 数据权限
     */
    private DataScopeFilter dataScopeFilter;


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public DataScopeFilter getDataScopeFilter() {
        return dataScopeFilter;
    }

    public void setDataScopeFilter(DataScopeFilter dataScopeFilter) {
        this.dataScopeFilter = dataScopeFilter;
    }

sql注入形成过滤: 

<select id="listByCondition" resultMap="CustomResultMap"
          parameterType="cn.daily.system.dto.request.WorkLogRequest">
    SELECT
        wl.*
    FROM
          t_work_log wl
    LEFT JOIN t_sys_user u ON wl.user_id = u.id
    <where>
          <if test="userName != null and userName != ''">
            AND  u.user_name = #{userName, jdbcType=VARCHAR}
          </if>
          <if test="dataScopeFilter != null and dataScopeFilter.condition != null">
             AND ${dataScopeFilter.condition}
          </if>
    </where>
  </select>

   至此,数据权限能依据配置形成过滤条件。如有更好的解决途径欢迎交流指导。

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页