数据权限实现

(企业内部)开发经验 专栏收录该内容
11 篇文章 3 订阅

权限框架可以根据用户所属角色决定有权限看到的菜单资源权限。
同一个资源下的同一个菜单的数据权限需要单独处理。

案例:一部门的张三和二部门的李四都是普通用户角色,普通用户都有用户管理的查询权限,但是,一部门的张三只能看到一部门以及一部门下面的数据的权限。

效果图:
测试部门的test001用户之后能看到自己部门的数据,需求部门的数据看不到
在这里插入图片描述
在这里插入图片描述

一、数据权限模型
1. 实现原理
1.创建一个需要执行数据权限的标志
2.根据数据权限标识对原查询SQL,在查询数据库之前进行特殊处理
3.利用拦截器在使用mapper在查询数据库时进行拦截处理
  1>获取所有的查询SQL
  2>获取查询sql中的的数据权限标识
    无:放行
    有:动态拼接数据权限sql
2. 实现流程
1.创建一个需要执行数据权限的标志对象DataScope
2.除超级管理员外,进行数据权限处理
3.获取当前用户的所处部门ID,根据部门ID获取当前部门以及子部门的ID集合
4.创建数据范围的拦截器DataScopeInterceptor
5.再判断根据数据权限标识对原查询SQL,利用拦截器在使用mapper在查询数据库时进行拦截处理
  1>获取所有的查询SQL
  2>获取查询sql中的的数据权限标识
    无:放行
    有:动态拼接数据权限sql
    简言之:将源sql看做一个查询整体,最后将结果集按照部门ids集合进行模糊区配筛选出(当前部门以及子部门的权限范围)
3. 权限标识对象
package cn.stylefeng.roses.core.datascope;

import java.util.List;

/**
 * 数据范围
 *
 * @author fengshuonan
 * @date 2017-07-23 22:19
 */
public class DataScope {

    /**
     * 限制范围的字段名称
     */
    private String scopeName = "deptid";

    /**
     * 具体的数据范围
     */
    private List<Long> deptIds;

    public DataScope() {
    }

    public DataScope(List<Long> deptIds) {
        this.deptIds = deptIds;
    }

    public DataScope(String scopeName, List<Long> deptIds) {
        this.scopeName = scopeName;
        this.deptIds = deptIds;
    }

    public List<Long> getDeptIds() {
        return deptIds;
    }

    public void setDeptIds(List<Long> deptIds) {
        this.deptIds = deptIds;
    }

    public String getScopeName() {
        return scopeName;
    }

    public void setScopeName(String scopeName) {
        this.scopeName = scopeName;
    }
}
4. 查询SQL拦截器
5. 查询SQL特殊处理
package cn.stylefeng.roses.core.datascope;


import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
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.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 数据范围的拦截器
 *
 * @author fengshuonan
 * @date 2017-07-23 21:26
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");

        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();

        //查找参数中包含DataScope类型的参数
        DataScope dataScope = findDataScopeObject(parameterObject);

        if (dataScope == null) {
            return invocation.proceed();
        } else {
            String scopeName = dataScope.getScopeName();
            List<Long> deptIds = dataScope.getDeptIds();
            String join = CollectionUtil.join(deptIds, ",");
            originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
            metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
            return invocation.proceed();
        }
    }

    /**
     * 查找参数是否包括DataScope对象
     */
    public DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
二、数据权限使用
2.1. 控制层
/**
     * 查询管理员列表
     *
     * @author gblfy
     * @Date 2020/12/24 22:43
     */
    @RequestMapping("/list")
    @Permission
    @ResponseBody
    public Object list(@RequestParam(required = false) String name,
                       @RequestParam(required = false) String timeLimit,
                       @RequestParam(required = false) Long deptId) {

        //拼接查询条件
        String beginTime = "";
        String endTime = "";

        if (ToolUtil.isNotEmpty(timeLimit)) {
            String[] split = timeLimit.split(" - ");
            beginTime = split[0];
            endTime = split[1];
        }

        if (ShiroKit.isAdmin()) {
            Page<Map<String, Object>> users = userService.selectUsers(null, name, beginTime, endTime, deptId);
            Page wrapped = new UserWrapper(users).wrap();
            return LayuiPageFactory.createPageInfo(wrapped);
        } else {
            DataScope dataScope = new DataScope(ShiroKit.getDeptDataScope());
            Page<Map<String, Object>> users = userService.selectUsers(dataScope, name, beginTime, endTime, deptId);
            Page wrapped = new UserWrapper(users).wrap();
            return LayuiPageFactory.createPageInfo(wrapped);
        }
    }

2.2. service
/**
     * 根据条件查询用户列表
     *
     * @author gblfy
     * @Date 2020/12/24 22:45
     */
    public Page<Map<String, Object>> selectUsers(DataScope dataScope, String name, String beginTime, String endTime, Long deptId) {
        Page page = LayuiPageFactory.defaultPage();
        return this.baseMapper.selectUsers(page, dataScope, name, beginTime, endTime, deptId);
    }
2.3. mapper
/**
     * 根据条件查询用户列表
     */
    Page<Map<String, Object>> selectUsers(@Param("page") Page page, @Param("dataScope") DataScope dataScope, @Param("name") String name, @Param("beginTime") String beginTime, @Param("endTime") String endTime, @Param("deptId") Long deptId);

<select id="selectUsers" resultType="map">
        select
        <include refid="Base_Column_List"/>
        from sys_user
        where status != 'DELETED'
        <if test="name != null and name != ''">
            and (phone like CONCAT('%',#{name},'%')
            or account like CONCAT('%',#{name},'%')
            or name like CONCAT('%',#{name},'%'))
        </if>
        <if test="deptId != null and deptId != 0">
            and (dept_id = #{deptId} or dept_id in ( select dept_id from sys_dept where pids like CONCAT('%$[', #{deptId}, '$]%') escape '$' ))
        </if>
        <if test="beginTime != null and beginTime != '' and endTime != null and endTime != ''">
            and (create_time between CONCAT(#{beginTime},' 00:00:00') and CONCAT(#{endTime},' 23:59:59'))
        </if>
    </select>
2.4. 获取部门集合
    /**
     * 获取当前用户的部门数据范围的集合
     */
    public static List<Long> getDeptDataScope() {
        Long deptId = getUser().getDeptId();
        List<Long> subDeptIds = ConstantFactory.me().getSubDeptId(deptId);
        subDeptIds.add(deptId);
        return subDeptIds;
    }

   /**
     * 获取子部门id
     */
 @Override
    public List<Long> getSubDeptId(Long deptId) {
        ArrayList<Long> deptIds = new ArrayList<>();

        if (deptId == null) {
            return deptIds;
        } else {
            List<Dept> depts = this.deptMapper.likePids(deptId);
            if (depts != null && depts.size() > 0) {
                for (Dept dept : depts) {
                    deptIds.add(dept.getDeptId());
                }
            }

            return deptIds;
        }
    }
2.5.
三、实战演练
3.1. 获取当前用户的所属部门

在这里插入图片描述

3.2. 获取当前用户的所属部门以及子部门集合

在这里插入图片描述
在这里插入图片描述

3.3. 调用逻辑service层执行查询逻辑

在这里插入图片描述

3.4. 调用mapper执行查询逻辑

在这里插入图片描述

3.5. 在查询数据库之前拦截处理

在这里插入图片描述

3.6. 未处理的原sql
select
         
        user_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"
     
        from sys_user
        where status != 'DELETED'
3.7. 处理后的sql
select * from (select
         
        user_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"
     
        from sys_user
        where status != 'DELETED') temp_data_scope where temp_data_scope.deptid in (26)
展开阅读全文
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

<p> <strong><span style="font-size:24px;">课程简介:</span></strong><br /> <span style="font-size:18px;">历经半个多月的时间,</span><span style="font-size:18px;">Debug</span><span style="font-size:18px;">亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。</span><span></span> </p> <p> <span style="font-size:18px;">其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,</span><span style="font-size:18px;">Debug</span><span style="font-size:18px;">还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程!</span><span></span> </p> <p> <br /> </p> <p> <span style="font-size:24px;"><strong>核心技术栈列表</strong></span><span style="font-size:24px;"><strong>:</strong></span> </p> <p> <br /> </p> <p> <span style="font-size:18px;">值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括</span><span style="font-size:18px;">Spring Boot</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Spring MVC</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Mybatis</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Mybatis-Plus</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Shiro(</span><span style="font-size:18px;">身份认证与资源授权跟会话等等</span><span style="font-size:18px;">)</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Spring AOP</span><span style="font-size:18px;">、防止</span><span style="font-size:18px;">XSS</span><span style="font-size:18px;">攻击、防止</span><span style="font-size:18px;">SQL</span><span style="font-size:18px;">注入攻击、过滤器</span><span style="font-size:18px;">Filter</span><span style="font-size:18px;">、验证码</span><span style="font-size:18px;">Kaptcha</span><span style="font-size:18px;">、热部署插件</span><span style="font-size:18px;">Devtools</span><span style="font-size:18px;">、</span><span style="font-size:18px;">POI</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Vue</span><span style="font-size:18px;">、</span><span style="font-size:18px;">LayUI</span><span style="font-size:18px;">、</span><span style="font-size:18px;">ElementUI</span><span style="font-size:18px;">、</span><span style="font-size:18px;">JQuery</span><span style="font-size:18px;">、</span><span style="font-size:18px;">HTML</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Bootstrap</span><span style="font-size:18px;">、</span><span style="font-size:18px;">Freemarker</span><span style="font-size:18px;">、一键打包部署运行工具</span><span style="font-size:18px;">Wagon</span><span style="font-size:18px;">等等,如下图所示:</span><span></span> </p> <img src="https://img-bss.csdn.net/201908070402564453.png" alt="" /> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p> <p> <span style="font-size:24px;">课程内容与收益</span><span style="font-size:24px;">:</span><span></span> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070403452052.png" alt="" /> </p> <p> <span style="font-size:18px;">总的来说,</span><span style="font-size:18px;">本课程是一门具有很强实践性质的“项目实战”课程,即“</span><span style="font-size:18px;">企业应用员工角色权限管理平台</span><span style="font-size:18px;">”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于</span><span style="font-size:18px;">Shiro</span><span style="font-size:18px;">的资源授权实现员工</span><span style="font-size:18px;">-</span><span style="font-size:18px;">角色</span><span style="font-size:18px;">-</span><span style="font-size:18px;">操作权限、员工</span><span style="font-size:18px;">-</span><span style="font-size:18px;">角色</span><span style="font-size:18px;">-</span><span style="font-size:18px;">数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图:</span> </p> <p> <span></span> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070404285736.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p> <p> <span style="font-size:18px;"><strong>以下为项目整体的运行效果截图:</strong></span> <span></span> </p> <img src="https://img-bss.csdn.net/201908070404538119.png" alt="" /> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405002904.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405078322.png" alt="" /> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405172638.png" alt="" /> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405289855.png" alt="" /> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405404509.png" alt="" /> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdn.net/201908070405523495.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p> <p style="text-align:left;"> <span style="font-size:18px;">值得一提的是,在本课程中,</span><span style="font-size:18px;">Debug</span><span style="font-size:18px;">也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:</span><span></span> </p> <img src="https://img-bss.csdn.net/201908070406328884.png" alt="" /> <p> <br /> </p>
相关推荐
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值