精华内容
下载资源
问答
  • 数据权限包括哪些
    千次阅读
    2019-06-06 18:19:18

    权限控制主要分为两块,认证(Authentication)与授权(Authorization)。认证之后确认了身份正确,业务系统就会进行授权,现在业界比较流行的模型就是RBAC(Role-Based Access Control)。RBAC包含为下面四个要素:用户、角色、权限、资源。用户是源头,资源是目标,用户绑定至角色,资源与权限关联,最终将角色与权限关联,就形成了比较完整灵活的权限控制模型。
    资源是最终需要控制的标的物,但是我们在一个业务系统中要将哪些元素作为待控制的资源呢?我将系统中待控制的资源分为三类:

    1. URL访问资源(接口以及网页)
    2. 界面元素资源(增删改查导入导出的按钮,重要的业务数据展示与否等)
    3. 数据资源

    现在业内普遍的实现方案实际上很粗放,就是单纯的“菜单控制”,通过菜单显示与否来达到控制权限的目的。
    我仔细分析过,现在大家做的平台分为To C和To B两种:

    1. To C一般不会有太多的复杂权限控制,甚至大部分连菜单控制都不用,全部都可以访问。
    2. To B一般都不是开放的,只要做好认证关口,能够进入系统的只有内部员工。大部分企业内部的员工互联网知识有限,而且作为内部员工不敢对系统进行破坏性的尝试。

    所以针对现在的情况,考虑成本与产出,大部分设计者也不愿意在权限上进行太多的研发力量。
    菜单和界面元素一般都是由前端编码配合存储数据实现,URL访问资源的控制也有一些框架比如SpringSecurity,Shiro。
    目前我还没有找到过数据权限控制的框架或者方法,所以自己整理了一份。

    数据权限控制原理

    数据权限控制最终的效果是会要求在同一个数据请求方法中,根据不同的权限返回不同的数据集,而且无需并且不能由研发编码控制。这样大家的第一想法应该就是AOP,拦截所有的底层方法,加入过滤条件。这样的方式兼容性较强,但是复杂程度也会更高。我们这套系统中,采用的是利用Mybatis的plugin机制,在底层SQL解析时替换增加过滤条件。
    这样一套控制机制存在很明显的优缺点,首先缺点:

    1. 适用性有限,基于底层的Mybatis。
    2. 方言有限,针对了某种数据库(我们使用Mysql),而且由于需要在底层解析处理条件所以有可能造成不同的数据库不能兼容。当然Redis和NoSQL也无法限制。

    当然,假如你现在就用Mybatis,而且数据库使用的是Mysql,这方面就没有太大影响了。

    接下来说说优点:

    1. 减少了接口数量及接口复杂度。原本针对不同的角色,可能会区分不同的接口或者在接口实现时利用流程控制逻辑来区分不同的条件。有了数据权限控制,代码中只用写基本逻辑,权限过滤由底层机制自动处理。
    2. 提高了数据权限控制的灵活性。例如原本只有主管能查本部门下组织架构/订单数据,现在新增助理角色,能够查询本部门下组织架构,不能查询订单。这样的话普通的写法就需要调整逻辑控制,使用数据权限控制的话,直接修改配置就好。

    数据权限实现

    上一节就提及了实现原理,是基于Mybatis的plugins(查看官方文档)实现。

    MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
    Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    ParameterHandler (getParameterObject, setParameters)
    ResultSetHandler (handleResultSets, handleOutputParameters)
    StatementHandler (prepare, parameterize, batch, update, query)

    Mybatis的插件机制目前比较出名的实现应该就是PageHelper项目了,在做这个实现的时候也参考了PageHelper项目的实现方式。所以权限控制插件的类命名为PermissionHelper。
    机制是依托于Mybatis的plugins机制,实际SQL处理的时候基于jsqlparser这个包。
    设计中包含两个类,一个是保存角色与权限的实体类命名为PermissionRule,一个是根据实体变更底层SQL语句的主体方法类PermissionHelper。

    首先来看下PermissionRule的结构:

    public class PermissionRule {
    
        private static final Log log = LogFactory.getLog(PermissionRule.class);
        /**
         * codeName<br>
         * 适用角色列表<br>
         * 格式如: ,RoleA,RoleB,
         */
        private String roles;
        /**
         * codeValue<br>
         * 主实体,多表联合
         * 格式如: ,SystemCode,User,
         */
        private String fromEntity;
        /**
         * codeDesc<br>
         * 过滤表达式字段, <br>
         * <code>{uid}</code>会自动替换为当前用户的userId<br>
         * <code>{me}</code> main entity 主实体名称
         * <code>{me.a}</code> main entity alias 主实体别名
         * 格式如:
         * <ul>
         * <li>userId = {uid}</li>
         * <li>(userId = {uid} AND authType > 3)</li>
         * <li>((userId = {uid} AND authType) > 3 OR (dept in (select dept from depts where manager.id = {uid})))</li>
         * </ul>
         */
        private String exps;
    
        /**
         * codeShowName<br>
         * 规则说明
         */
        private String ruleComment;
    
    }
    

    看完这个结构,基本能够理解设计的思路了。数据结构中保存如下几个字段:

    • 角色列表:需要使用此规则的角色,可以多个,使用英文逗号隔开。
    • 实体列表:对应的规则应用的实体(这里指的是表结构中的表名,可能你的实体是驼峰而数据库是蛇形,所以这里要放蛇形那个),可以多个,使用英文逗号隔开。
    • 表达式:表达式就是数据权限控制的核心了。简单的说这里的表达式就是一段SQL语句,其中设置了一些可替换值,底层会用对应运行时的变量替换对应内容,从而达到增加条件的效果。
    • 规则说明:单纯的一个说明字段。

    核心流程
    系统启动时,首先从数据库加载出所有的规则。底层利用插件机制来拦截所有的查询语句,进入查询拦截方法后,首先根据当前用户的权限列表筛选出PermissionRule列表,然后循环列表中的规则,对语句中符合实体列表的表进行条件增加,最终生成处理后的SQL语句,退出拦截器,Mybatis执行处理后SQL并返回结果。

    讲完PermissionRule,再来看看PermissionHelper,首先是头:

    @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
            @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
    public class PermissionHelper implements Interceptor {
    }
    

    头部只是标准的Mybatis拦截器写法,注解中的Signature决定了你的代码对哪些方法拦截,update实际上针对**修改(Update)、删除(Delete)生效,query是对查询(Select)**生效。

    下面给出针对Select注入查询条件限制的完整代码:

    
        private String processSelectSql(String sql, List<PermissionRule> rules, UserDefaultZimpl principal) {
            try {
                String replaceSql = null;
                Select select = (Select) CCJSqlParserUtil.parse(sql);
                PlainSelect selectBody = (PlainSelect) select.getSelectBody();
                String mainTable = null;
                if (selectBody.getFromItem() instanceof Table) {
                    mainTable = ((Table) selectBody.getFromItem()).getName().replace("`", "");
                } else if (selectBody.getFromItem() instanceof SubSelect) {
                    replaceSql = processSelectSql(((SubSelect) selectBody.getFromItem()).getSelectBody().toString(), rules, principal);
                }
                if (!ValidUtil.isEmpty(replaceSql)) {
                    sql = sql.replace(((SubSelect) selectBody.getFromItem()).getSelectBody().toString(), replaceSql);
                }
                String mainTableAlias = mainTable;
                try {
                    mainTableAlias = selectBody.getFromItem().getAlias().getName();
                } catch (Exception e) {
                    log.debug("当前sql中, " + mainTable + " 没有设置别名");
                }
    
    
                String condExpr = null;
                PermissionRule realRuls = null;
                for (PermissionRule rule :
                        rules) {
                    for (Object roleStr :
                            principal.getRoles()) {
                        if (rule.getRoles().indexOf("," + roleStr + ",") != -1) {
                            if (rule.getFromEntity().indexOf("," + mainTable + ",") != -1) {
                                // 若主表匹配规则主体,则直接使用本规则
                                realRuls = rule;
    
                                condExpr = rule.getExps().replace("{uid}", UserDefaultUtil.getUserId().toString()).replace("{bid}", UserDefaultUtil.getBusinessId().toString()).replace("{me}", mainTable).replace("{me.a}", mainTableAlias);
                                if (selectBody.getWhere() == null) {
                                    selectBody.setWhere(CCJSqlParserUtil.parseCondExpression(condExpr));
                                } else {
                                    AndExpression and = new AndExpression(selectBody.getWhere(), CCJSqlParserUtil.parseCondExpression(condExpr));
                                    selectBody.setWhere(and);
                                }
                            }
    
                            try {
                                String joinTable = null;
                                String joinTableAlias = null;
                                for (Join j :
                                        selectBody.getJoins()) {
                                    if (rule.getFromEntity().indexOf("," + ((Table) j.getRightItem()).getName() + ",") != -1) {
                                        // 当主表不能匹配时,匹配所有join,使用符合条件的第一个表的规则。
                                        realRuls = rule;
                                        joinTable = ((Table) j.getRightItem()).getName();
                                        joinTableAlias = j.getRightItem().getAlias().getName();
    
                                        condExpr = rule.getExps().replace("{uid}", UserDefaultUtil.getUserId().toString()).replace("{bid}", UserDefaultUtil.getBusinessId().toString()).replace("{me}", joinTable).replace("{me.a}", joinTableAlias);
                                        if (j.getOnExpression() == null) {
                                            j.setOnExpression(CCJSqlParserUtil.parseCondExpression(condExpr));
                                        } else {
                                            AndExpression and = new AndExpression(j.getOnExpression(), CCJSqlParserUtil.parseCondExpression(condExpr));
                                            j.setOnExpression(and);
                                        }
                                    }
                                }
                            } catch (Exception e) {
                                log.debug("当前sql没有join的部分!");
                            }
                        }
                    }
                }
                if (realRuls == null) return sql; // 没有合适规则直接退出。
    
                if (sql.indexOf("limit ?,?") != -1 && select.toString().indexOf("LIMIT ? OFFSET ?") != -1) {
                    sql = select.toString().replace("LIMIT ? OFFSET ?", "limit ?,?");
                } else {
                    sql = select.toString();
                }
    
            } catch (JSQLParserException e) {
                log.error("change sql error .", e);
            }
            return sql;
        }
    

    重点思路
    重点其实就在于Sql的解析和条件注入,使用开源项目JSqlParser

    • 解析出MainTable和JoinTable。from之后跟着的称为MainTable,join之后跟着的称为JoinTable。这两个就是我们PermissionRule需要匹配的表名,PermissionRule::fromEntity字段。
    • 解析出MainTable的where和JoinTable的on后面的条件。使用and连接原本的条件和待注入的条件,PermissionRule::exps字段。
    • 使用当前登录的用户信息(放在缓存中),替换条件表达式中的值。
    • 某些情况需要忽略权限,可以考虑使用ThreadLocal(单机)/Redis(集群)来控制。

    结束语

    想要达到无感知的数据权限控制,只有机制控制这么一条路。本文选择的是通过底层拦截Sql语句,并且针对对应表注入条件语句这么一种做法。应该是非常经济的做法,只是基于文本处理,不会给系统带来太大的负担,而且能够达到理想中的效果。大家也可以提出其他的见解和思路。

    更多相关内容
  • 中后台学习笔记 – 数据权限

    千次阅读 2022-01-24 11:22:50
    编辑导语:我们常常在数据权限中找不到合适的门路,中后台的数据权限该怎么设计才能够满足我们所需业务的数据权限架构体系?作者给我们从三个方面讲解了有关数据权限的知识,我们一起来看下吧。 曾经看到过这样一个...

    编辑导语:我们常常在数据权限中找不到合适的门路,中后台的数据权限该怎么设计才能够满足我们所需业务的数据权限架构体系?作者给我们从三个方面讲解了有关数据权限的知识,我们一起来看下吧。

    曾经看到过这样一个需求:他想知道,我“为什么”能够看到这条数据? 如果这条数据是我自己创建的,那么其他能看到这条数据的人,都由于什么原因呢?

    我的第一反应是:数据权限设计的太复杂了。

    随后又想到: “为什么数据权限要设计的这么复杂呢,有没有更好的实现方案?”

    但在回答这个问题之前,可能要先完成另一件事情,分析清楚到底什么是数据权限? 都有哪些业务场景,需要数据权限的支持?

    简单来讲,数据权限就是“用户是否能够查看数据”,主要是为了业务系统的信息安全考虑,不同的人,在不同的场景下,所能看到的数据范围也不一样。

    为了行文方便,后续统一使用“查看数据”进行描述,实际上像增/删/改/查/锁定/解锁/分享等的数据权限类型有很多,就不在本文中介绍了。

    本文将结合在工作中遇见的一些业务场景,跟大家分享一些常见的数据权限设计,也希望能够总结出一套,最符合本公司业务的数据权限架构体系。

    一、基础数据权限

    1. 对象级权限
    基础数据权限通常会承载到一个权限媒介上,比如说权限集,职能等。他通常包括两部分,对象级权限与字段级权限。

    简单来讲,对象级权限可以控制到一个用户是否能够看到某一个业务菜单或者业务Tab,也就是说用户甚至可以不知道系统中有这一类的数据。对象级权限的设计会包含下面几部分。

    在这里插入图片描述
    当然,对于不同的企业,可能还会有更精细的管理,比如针对数据的转移,锁定/解锁等基本功能可以也需要做控制。

    关于“查询所有关联”和 “修改所有关联”,他的权限优先级是要高于其他权限规则。比如商机、订单、报价单等业务对象都关联了客户,那么如果在客户上设置了“查询所有关联”的话,就会覆盖掉具体关联业务实体设置的对象级权限。

    2. 字段级权限
    根据业务需要,即使可以看到某个业务对象,但也需要对某些隐私字段做数据权限处理。 例如:联系人电话、薪水等信息,不会对所有人开放。
    在这里插入图片描述
    如果企业的系统中,有包含页面布局的相关设置,那么字段级权限设置,要覆盖掉布局的设置。

    二、记录级数据权限

    其实对于很多业务系统来说,可能上面的基础数据权限设计已经能够满足大部分数据权限场景了,但问题是有一类“特殊场景”解决不了。

    某“一部分”数据,只允许“一部分”人查看。

    几乎所有复杂的数据权限设计,都是为了解决这个问题,而且因为使用场景差异太大,很难找到一个一揽子解决方案。所以需要多种方案结合,才能在便捷性、安全性上,满足中大企业复杂的数据权限要求。

    1. 系统默认权限
    要想解决 某“一部分”数据,只允许“一部分”人操作,理论上那就意味着每一条数据都应该可以被控制。

    那么 每一个用户与每一条数据之间到底是什么关系,是业务系统需要回答的第一个问题。

    通常来讲,系统默认权限应该是“最严格的”,“唯一”的“限制”数据权限的方式,其他的记录级数据权限,应该是在系统默认权限的基础上,增加额外的数据权限。

    比如最极端的限制条件,所有的业务数据都是不可见的。 当然对于某些公司来讲,一些数据是全员可见的,比如像公告、通讯录、社区帖子等类型的数据。

    系统默认权限如果需要修改,应该要受到严格的控制流程管理,毕竟这是整个数据权限架构中,唯一的限制。

    2. 记录所属人
    通常来说,如果一个用户拥有某个业务实体创建对象的权限,那么针对这条特定的记录,用户创建完成记录后,会自动变为这条记录的所属人(owner)。

    在系统默认权限的基础上,记录的所属人就属于一种新增的数据权限,可以对这个用户进行查看操作(可进行的操作应该基于基础数据权限)。

    由于业务的需要,记录会在不同的用户之间进行数据转移的操作(transfer ownership),所以记录所属人,不一定是记录的创建人。

    还有一种例外情况,一条记录不属于某个特定的用户。

    在有些公司的业务中,会有类似公海池、队列等记录分配器功能。

    那么在分配记录之前,这些记录也是可以属于这些分配器的。

    相关功能的成员可以在列表视图中看到这些分配器中的记录数据,并进行所属人认领的操作(更多详细内容,我会更新在后续的公海池设计当中)。

    3. 部门/相关部门
    每个企业的组织架构都会区分部门,那么就有一个非常自然的需求场景:看到本部门以及下级部门相关的所有数据。

    一种比较简单的做法,就是在创建数据的时候,自动将数据关联到创建用户的所属部门上。

    还有一些情况,有些公司的数据是由专门的团队负责创建的,那么针对于这些数据,就需要可以变更数据的所属部门的能力。

    随着业务的不断发展,可能会遇到跨部门数据查看的需求,也就是说,一个用户有主要的所属部门,以及根据数据权限需要而设置的多个相关部门。

    4. 直属负责人/助理
    在传统的组织架构中,除了部门层级之间的数据共享需求之外,还有人员汇报关系线。

    通常的做法,就是在为每一个用户,指定一个直属上级负责人,直属上级负责人会被赋予所有下属层级的相同数据权限。

    为了管理方便,也可以为直属负责人设置助理,助理与直属负责人具备相同的数据权限,这样更灵活的让不同的用户能够跳脱出公司组织架构的束缚。

    5. 岗位层级
    使用部门权限体系会带来一个问题,企业的组织架构往往与业务的架构是不一致的。而且业务架构会经常变化,而结构变化对于数据权限控制来讲,是一个挑战。

    岗位层级,可以根据数据权限需要,将一个用户或者一组用户放在同样的岗位下。

    这样的话,在创建岗位的同时,也可以很好的了解公司架构到底应该以什么样的形式组织在一起更高效。

    可以从上至下的设置岗位层级,比如说最高层岗位叫CEO,可以查看整个组织的数据。然后以事业部总经理或者地区负责人的岗位,继续向下细分,查看各自岗位下的数据。

    岗位层级权限还有一个更加强大的能力,就是岗位与数据之间可以是 m:n 关系,也就是说同一条数据可以同时共享给多个岗位,满足更加复杂的数据共享场景。

    岗位层级的设计,可以将数据与用户解绑,这在人员变动,业务调整时,可以很方便的做数据共享的变更。
    在这里插入图片描述
    6. 共享规则
    对于数据权限还有一类更加接近直觉的设计方式,也就是别搞那些花里胡哨的概念,我就是要把“这一部分”数据,共享给“那一部分”人,简单直接一点,怎么做?

    换句话来讲,如果上面的权限设计体系统统不能满足业务场景,可不可以提供一种直接的数据共享方式。

    “共享规则”的设计方案可能是一种解决思路。通常来讲,共享规则解决了两类场景:

    把特定用户的数据,共享给特定用户。
    把符合特定条件的数据,共享给特定用户。
    当然这里面的“特定用户”,不仅仅只用户本身,所有包含用户的容器概念元素(部门、群组、角色等),应该都包含在内。
    在这里插入图片描述
    7. 手工共享
    共享规则虽然方便,但解决不了一类更要命的数据权限场景,有些共享场景是没有办法提前可以预想的,是随机的,是随时随地的,怎么办?

    其实这种场景的前提条件就决定了权限设计的方式,既然规则不行,那就手工来指定好了。

    手工共享的数据权限设计,完全是基于记录所属人的自由意志,记录所属人认为数据应该共享给谁,就共享给谁好了。

    需要注意的是,当记录的所属关系发生变更时,那么手工共享关系应该直接解除。记录所属人也需要可以随时查看和修改,当前这条数据的手工共享情况。

    8. 团队成员
    手工共享的确可以解决非常态化规则的数据权限场景,但麻烦的是,共享关系会随着记录所属人的所属关系转移,而全部丢失。

    对于一条记录来讲,如果大部分需要共享的用户不会经常发生变动,可以尝试使用团队成员的方式来进行手工共享。

    记录所属人或者有记录编辑权限的团队成员,可以同时添加其他用户作为这条记录的团队成员,记录会对所有团队成员共享。

    这样即使记录所属人发生变化,其他的团队成员会仍然保留与此条记录的数据共享关系。

    团队成员除了支持用户外,也需要支持部门,群组才能更灵活的支撑单条数据的共享。

    9. 群组共享
    团队成员共享方案,能够解决大部分需要灵活配置的单条数据共享场景,但操作比较繁琐。

    如果需要频繁共享给一个虚拟组织团队,就需要在每一条数据上添加若干个,相同的团队成员,效率很低。

    群组就会很好地解决这个问题,若几个用户之间需要经常共享数据,那么可以将用户们用群组圈起来,记录的所属人,可以通过将自己创建的、符合条件的数据,自动共享到若干群组中,用户也可以通过手动共享的方式,将某条数据共享到群组中。

    而只有群组成员才能看到群组中的数据,群组一般分为公用群组和私用群组。

    公用群组是整个公司都能看到的群组,任何人都可以将数据共享到公用群组中。
    私用群组是每个人独立创建的,只有群组成员可以将数据共享到私用群组中。
    群组应该是可以支持嵌套关系的,比如:用户、角色、岗位、群组本身,都是可以是群组的一员。

    当然处于性能的考虑,最好对类似群组套群组的这种方式,做一个层级上的限制。

    群组与数据的关系应该是多对多的,也就是说,同样一条数据,可以在不同的群组中做共享。

    群组数据的共享应该是独立的,通过类似其他岗位层级,直属上级相关的其他权限,是无法突破群组成员限制的。也就是说UserA是群组成员,可以看到群组的数据,但UserA的上司由于不是群组成员,所以看不到群组的数据。

    10. 区域层级
    对于大型企业应用场景,经常会出现销售、产品、服务分属于不同的业务单元,但需要根据不同的规则共享客户等相关资源。那么就可以创建多个区域层级,让同一个客户按照不同的层级结构共享给各个不同的业务单元。

    与岗位层级类似,区域层级通常应用于地盘资源管理的数据权限划分,通过多个不同的区域维度规则,如地区、客户等级、行业、产品线。相应的数据在创建/编辑后,可以自动进入到符合规则的区域。

    通过一些级联跟随分配的设置,数据下的相关数据也可以同时进入到相同区域,比如客户下的订单,订单下的回款单等。

    数据进入到区域后,区域成员就可以看到数据了。

    一般来讲,区域权限分为2级,Object、Hirerachy。

    Object 业务对象级权限,说的是一个区域成员针对于具体的业务对象,是什么权限。比如针对于客户,是查看还是编辑?
    Hirerachy 层级权限,是说针对于业务实体,除了本级区域的权限,是否也具备下级区域的权限。
    针对于单个区域,若有业务需要,也可以添加 Record 级别的权限,类似于记录所属人。

    针对于每一个区域,也可以设置这个区域的记录负责人(Territory Record Owner),这样对于基于区域管理的业务属性来讲,可以将记录做到最灵活的配置。

    三、如何应用数据权限

    回到最初提到的问题,他们的深层考虑是什么?

    如果这条数据是我自己创建的,那么其他能看到这条数据的人都有谁?

    对于很多企业来讲,从数据隐私安全的角度考虑到数据权限问题,是很重要的命题。

    对数据权限因果链条的掌握,可以对整个数据权限体系的优化,起到至关重要的指导作用。

    “小王已经从A事业部转岗,还能通过助理权限看到B事业部的客户呢,应该马上取消小王的权限。”
    “小张是C事业部的销售成员,通过群组权限看到他本应看到的客户,而不是岗位权限看到这个客户,看来客户的分配逻辑有一些问题。”
    有人想知道,我为什么能够看到这条数据?

    当用户看到这条数据时,通过看到这条数据的原因,可以快速的找准自己的职责定位。

    因为我是这个客户的团队成员,那么我需要对这个客户的培育投入精力。

    这个客户是我下属自拓的一名新客户,那么我需要对其给予帮助。

    更重要的是,经常的审视数据权限体系的因果关系,也会对业务本身认识得更清晰,优秀的数据权限体系,甚至可以优化公司的业务运营体系,让效率提升自下而上的发生。

    那么问题来了,既然优秀的数据权限体系,可以提升管理效率,那有没有什么设计原则是可以遵守的?

    或者说作为一个公司业务运营的管理者,到底应该怎么设计公司的数据权限体系呢?

    在我们设计权限体系的时候,都是用各种概念将具体的权限,通过满足特定业务场景的方式包装起来。

    我们把包装起来的这样一个个的概念,可以称之为 “权限媒介”。

    权限媒介在自身属性上,分为三种类型:

    (1)Owner

    独立媒介,比如记录所属人,岗位层级。

    可以自主的满足所有的权限结构;
    可以包含其他权限体系。
    (2)Child

    子媒介,比如群组,区域。

    可以自主的满足所有的权限结构;
    可以被特定的Owner媒介包含;
    可以包含其他权限体系。
    3. Element

    原子媒介,比如助理/直属上级从属于部门层级。

    可以理解为内部权限媒介;
    仅可以包含其他原子媒介。
    权限媒介的自身属性类型可以帮助我们认识到,权限体系的设计是整体的,而不是混乱的。

    一个媒介已经被定为成Element,就不要同时还具备Owner的属性,不然可能虽然一时解决了问题,但今后的扩展会无比艰难。

    权限媒介在使用场景上,分为两种类型:

    (1)有规则的

    比如共享规则,岗位层级。

    根据公司的业务需要,总结出来的数据共享场景;
    可节省大量的权限共享设置成本。
    (2)无规则的

    比如团队成员,手工共享。

    人为认定的数据权限共享场景,提前无法认知;
    规则设置成本,超过了使用成本。
    权限媒介的使用场景类型,可以帮助我们认识到,权限的灵活性,要把“人”的判断纳入到整个体系中来。过高的设置成本不但会加大业务的复杂度,而且也会让“人”失去控制感。

    相信了解了权限媒介的自身属性和使用场景之后,那我们就可以试着总结出一套适合自己公司数据权限体系架构的最佳实践原则。

    让我们一起创造“有良好扩展性,并让人有灵活掌控感”的数据权限体系吧。

    展开全文
  • 从零开始java数据权限篇:数据权限

    千次阅读 2019-09-09 11:59:05
    一:数据权限的产生 二:数据权限的数据切割 1.数据对应的层级图 2.用户数据查询 3.用户流程管理 4.部门-岗位-公司查询拓扑图 三.说明 一:数据权限的产生 在一个后管系统中,由2个最重要的权限划分。第一...

    目录

    一:数据权限的产生

    二:数据权限的数据切割

    1.数据对应的层级图

    2.用户数据查询

    3.用户流程管理

    4.部门-岗位-公司查询拓扑图

    三.说明



    一:数据权限的产生

      在一个后管系统中,由2个最重要的权限划分。第一个访问权限,通过控制访问路径、请求来控制访问的权限,第二个是数据权限,通过一系列的分割策略来对用户进行管理。

     访问权限,可以这么说访问权限可以通过集成框架(比如shiro或者Spring seurity或者oauth2)来控制。所以严格意义上来说访问权限是一个技术框架

     数据权限,数据权限用来对用户信息进行管理包括但是不限于用户列表查询、子母公司数据划分以及工作流等的一系列。但是一个比较尴尬的问题就是,数据权限实则是一个业务层,它是由具体的公司业务来决定的。当然,凡是可以抽象出来的我们都可以抽象出来。目前主流的的数据权限控制以 部门-岗位-上级-本级 这样一个递减结构来做通用式的管理。

      也翻看了一些所谓的数据级权限中间件比如目前介绍较多的Ralasafe,实则上就是在访问Sql上做一些侵入式Sql限制。基于此,我们完全可以在项目中使用AOP做。但是很尴尬的是目前这个数据级的中间件已经停止维护。

     

    二:数据权限的数据切割

      在上面,我们谈到一般都是以 部门-岗位-上级-本级 的结构来做数据权限控制。但是一般项目中会把上级-本级合并产生一个简单的结构:部门-岗位-用户 来维护数据权限。

    1.数据对应的层级图

                                                   

      首先:部门对于用户而言,是用户固有的一个属性,只能是一对一关系。

                  岗位对于用户而言 ,是由上级或者管理员分配的一个属性,可以是一对多(即一个用户被指向多个部门的负责人,但是

                                                        他本身只有某一个部门的属性)。

                  部门对岗位而言,同时也是一对多的关系,即一个部门有多个角色。

    2.用户数据查询

                   

     然后,我们针对组织结构细分:

     (1)公司结构以及部门结构:管理层级(具有本部门的最高权限)以及一般员工(只有自我查询权限)

                                                           其中总公司的管理层具有整个结构的最高权限。

    (2)单个单位中的结构:单个单位内的负责人具有最高单位权限

                                                  单个单位的次级负责人具有管理名下的权限。(即上级-下级的管理权限)

                                                   单个单位内的普通员工只具有自身的权限

    3.用户流程管理

      由于后期要整合工作流,因此这里我们将流程梳理清晰。

     

    4.部门-岗位-公司查询拓扑图

    目前,主要角色分为以下几个:

    (1)普通员工(仅能看到自己的信息)

    (2)部门管理人(仅能看到本部门)

    (3)分公司董事(除了董事会部门的其他分公司所有的部门)

    (4)分公司总负责人(看到分公司所有的信息)

    (5)总公司董事会(除了总公司董事会之外的所有总,自公司的所有部门)

    (6)总董事

    部门-岗位-董事表拓扑(比较简单,就不做UML了)

    据图查询:

    (1)普通员工:这个就不用说了

    (2)部门负责人:查询 用户表中 部门id一样的即可

    (3)分公司董事会:第一步,根据用户表中的部门id去查询部门表

                                          第二步:根据部门表中的所有上级id,取出公司统一的开头(一般第一位为总公司id,第二位为

                                                          分公司id,第三位包括以后是部门层级一级一级玩下的id,以逗号隔开)

                                         第三步:根据公司统一的开头曲模糊查询所有的部门id(这里在程序中控制得当,是完全可以的)

                                         第四步:根据上一步的部门id,同时去掉本身的部门id(即董事会部门的id),然后反查用户表

     (4)分公司负责人 :在上一个的第四步,不去掉本身的部门id

     (5)总公司董事会:查询所有的,除了自身的部门id

    (6)总公司负责人:全查

    (7)自定义:选择部门,然后插入  岗位-部门表做联查(对,这个表只在这里用到)

    三.说明

      上面所描述的是通常情况下,一般都是根据具体业务做管理

     

     

    展开全文
  • 功能权限和数据权限管理的实现

    千次阅读 2018-11-28 13:32:05
    转 功能权限和数据权限管理的实现 2018年11月28日 11:36:19 更多 &amp;amp;lt;div class=&amp;quot;tags-box space&amp;quot;&...

    1      引言

    权限,可分为“功能(操作)权限”和数据权限两种,在系统中,两种权限应当同时有效。例如,在windows系统中,某用户具有新建一个文件的功能权限,该用户在C盘没有写权限,但在D盘有写权限;则该用户不能把他创建的文件保存在C盘而只能保存在D盘。

    在上述例子中,能否创建文件是由功能权限来控制的,能否保存文件是由数据权限进行控制的。只有两者同时有效,用户的业务才能顺利进行。

    简单地说,权限管理就是对资源的管理。权限管理的目的就是建立分配资源的规则,以便用户能够通过这套规则,获取他们应该获得的资源。

    1.1     定义

    ² 功能权限:

    也叫操作权限,指的是允许拒绝用户使用系统提供的某个功能。

    ² 数据权限:

    指的是允许拒绝用户进行某个数据的增删改查操作。

    ² 授权:

    指的是分配具体的权限给具体的人。

    ² 鉴权:

    指的是对具体人的行为,根据权限规则进行合法性鉴别。

    1.2     授权的基本原则

    对于授权来说,需要定义的有且只有权限和授权对象两个要素。简而述之,对于功能操作就是“什么功能授权给哪个用户来操作”。同样,对于数据,就是“什么数据授权给哪个用户来操作”

    一般情况下,我们并不会对单一的功能/数据进行单用户的授权管理,因为这样用户操作起来显然非常麻烦。为了方便和简化操作,一般的授权规则是:

    哪些功能/数据授权给哪些用户

    1.3     授权的一般方法

    在实际的授权管理中,我们总是根据业务的需求,将一些在业务上不可分割的、需要允许用户一起使用的功能,组合成一个权限集合进行统一授权。对于这样的权限集合,我们一般称之为“角色”。也就是说,我们通过角色来定义用户被允许使用哪些功能和访问哪些数据。当然,我们一般把功能和数据分开来进行授权,以便获得更加灵活的权限规则配置方法,以适应更广泛的授权需求。

    由于某些不同用户在该业务上需要具有相同的权限,那么这些不同的用户在特定的业务上就具有了共性,可以作为一个抽象的用户来进行权限的授予。授权管理使用的抽象的用户,也就是用户集合,除了普遍使用的“用户组”外,还可以引用别的业务中所使用的对象。例如组织机构管理中的“机构/部门”、“职位”和工作流中使用的“岗位”等,在授权管理中都是作为用户集合使用,本质毫无二致。

    通过让抽象的用户扮演角色,即可使这个抽象的用户所代表的真实用户获得完成业务所需的权限。通过这样的方式,可以简化授权管理,方便用户操作。

    2      授权管理

    将特定的权限授予特定的人群的过程,我们称之为“授权”。为了能够方便地进行授权操作,我们必须要有一个能够提供合理授权方法的用户界面。

    2.1    功能权限的授权

    对于一个可扩展的系统来说,意味着功能是不断变化的。为了适应这种不能事先确定的变化,必须将功能权限进行分散管理。分散管理的好处如下:

    ²  天然地支持业务的动态变化,系统实现简单。

    ²  权限的调整范围可控制在局部范围,方便权限的管理和操作。

    2.1.1   一般权限的授予

    功能权限是单维度的,可以通过简单的在功能列表或功能树上进行勾选来确定一个“角色”所允许的功能操作。然后,让相应用户成为该“角色”的“扮演者”。这样就可以把该角色所允许的功能操作授权给指定的用户了。

    如果需要有更多的不同角色,那么新建角色,勾选不同的被允许的功能操作,并分别让相应的用户成为新角色的成员即可。

    2.1.2   例外权限的授予

    如果某个特定用户张三需要额外的一个权限,那么新建一个允许该功能操作的角色,并让张三成为该角色成员即可使张三拥有额外的权限。

    如果某个特定用户李四需要比同一项目组的其他人少一个权限,那么新建一个拒绝该功能操作的角色,并让李四成为该角色成员即可使李四不能进行该项操作。因为在鉴权过程中,拒绝的优先级要高于允许。

    2.2    数据权限的授权

    数据在系统中共同的特性有如下维度:

    ²  业务维度:不同的业务产生不同的数据

    ²  生产者维度:相同的业务会有多个数据生产者和生产部门

    有些业务需要用户访问其他业务的数据,或者是其他数据生产者中特定生产者的生产的数据。简单的说,就是数据权限的授予必须支持跨业务和跨部门。

    2.2.1   一般权限的授予

    由于数据的特殊性质,实际上在有限范围内的授权比功能权限的授予更加方便。因为生产部门和生产者具有天然的分类属性,所以象“本机构”、“本部门”、“本人”这些对生产者维度的进一步抽象就有了用武之地。

    在不对业务维度做限定的情况下,就可以配置例如“允许本部门的成员管理(增删改查)本部门的数据”这样的权限规则。那么对于不同部门的用户,这条共同的规则所产生的效果并不相同,具体的效果是与用户所在的部门的业务和产生的数据相对应的。

    根据以上分析,我们可以内置一些抽象规则,例如:

    ²  允许管理本机构(含下级机构/部门)的数据

    ²  允许管理本部门(含下级部门)的数据

    ²  仅允许管理本部门的数据

    ²  仅允许管理本人的数据

    ²  允许查看本机构(含下级机构/部门)的数据

    ²  允许查看本部门(含下级部门)的数据

    ²  仅允许查看本部门的数据

    ²  仅允许查看本人的数据

    2.2.2   自定义权限的授予

    一般数据权限的授予只能局限于符合高度抽象规则所限定的范围。如果要在这个范围之外的数据进行授权,例如想让财务部的人访问采购部的数据,显然是一般数据授权所不能支持的。

    这个时候,我们就必须要提供用户在角色中自由定义允许或禁止用户访问的数据集的方法。上面说过,一个数据需要在业务和生产者两个维度上进行描述,才能确定数据。那么在定义角色所允许访问的数据集时,因为授权分散在不同的业务中,所以业务是确定的,剩下的就是需要指定一个或多个生产者。在这里,生产者可以使用抽象方法进行归纳分类。

    最后,类同于功能权限的授权方式,我们把定义好的角色分配给一个抽象的用户即可将一个自定义的数据权限授予抽象用户代表的真实用户。

    3      权限管理的实现

    权限管理的具体实现方法,离不开数据结构的支持。相对来说,有具体的数据结构,我们也更容易理解权限的管理机制。在讨论权限之前,我们还需要先了解权限管理的对象:功能资源和数据资源。这些资源同样需要一个数据结构去进行定义。

    3.1    组织机构和用户

    组织机构表:

    1. CREATE TABLE Sys_Organization(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_Organization UNIQUE CLUSTERED IDENTITY(1,1),                               --自增序列  
    4. [ParentId]         VARCHAR(36),                                                                                        --父节点ID  
    5. [NodeType]         INT NOT NULL,                                                                                       --节点类型:1、机构;2、部门;3、职位  
    6. [Index]            INT,                                                                                                --序号  
    7. [Code]             VARCHAR(32),                                                                                        --编码  
    8. [Name]             NVARCHAR(32) NOT NULL,                                                                              --名称  
    9. [Alias]            NVARCHAR(16),                                                                                       --别名/简称  
    10. [FullName]         NVARCHAR(32),                                                                                       --全称  
    11. [PostId]           VARCHAR(36),                                                                                        --岗位ID,字典  
    12. [Validity]         BIT DEFAULT 0 NOT NULL,                                                                             --是否有效:0、无效;1、有效  
    13. [CreatorUserId]    VARCHAR(36),                                                                                        --创建人ID  
    14. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    15. )  
    16. GO  

    用户表: 

    1. CREATE TABLE Sys_User(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED,                                                               --此ID与主数据ID相同  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_User UNIQUE CLUSTERED IDENTITY(1,1),                                       --自增序列  
    4. [Name]             NVARCHAR(16) NOT NULL,                                                                              --用户名  
    5. [LoginName]        VARCHAR(36) NOT NULL,                                                                               --登录名  
    6. [Password]         VARCHAR(32) DEFAULT 'e10adc3949ba59abbe56e057f20f883e' NOT NULL,                                    --登录密码,保存密码的MD5值,初始密码123456  
    7. [Description]      NVARCHAR(MAX),                                                                                      --描述  
    8. [BuiltIn]          BIT DEFAULT 0 NOT NULL,                                                                             --是否预置:0、自定;1、预置  
    9. [Validity]         BIT DEFAULT 0 NOT NULL,                                                                             --是否有效:0、无效;1、有效  
    10. [CreatorUserId]    VARCHAR(36),                                                                                        --创建人ID  
    11. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    12. )  
    13. GO  

    3.2    资源

    3.2.1   模块和功能

    模块表: 

    1. CREATE TABLE Sys_Module(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_Module UNIQUE CLUSTERED IDENTITY(1,1),                                     --自增序列  
    4. [ParentId]         VARCHAR(36),                                                                                        --父模块ID  
    5. [Level]            INT NOT NULL,                                                                                       --模块级别:0、主窗体模块;1、普通业务模块;2、业务子模块  
    6. [Name]             NVARCHAR(64) NOT NULL,                                                                              --名称  
    7. [Location]         VARCHAR(MAXNOT NULL,                                                                              --文件安装路径  
    8. [DataTable]        NVARCHAR(64),                                                                                       --模块主数据表名称  
    9. [Description]      NVARCHAR(MAX),                                                                                      --描述  
    10. [RegisterTime]     DATETIME DEFAULT GETDATE() NOT NULL                                                                 --模块注册时间  
    11. )  
    12. GO  

    模块功能表:
    1. CREATE TABLE Sys_ModuleAction(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_ModuleAction UNIQUE CLUSTERED IDENTITY(1,1),                               --自增序列  
    4. [ModuleId]         VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL,                       --模块注册ID  
    5. [Name]             NVARCHAR(64) NOT NULL,                                                                              --名称  
    6. [SubModuleId]      VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID),                                                  --子模块ID(功能作为子模块入口时)  
    7. [Description]      NVARCHAR(MAX)                                                                                       --描述  
    8. )  
    9. GO  

    3.2.2   业务数据

    物资数据表: 

    1. CREATE TABLE MDG_Material(  
    2. [MID]              VARCHAR(36) PRIMARY KEY NONCLUSTERED FOREIGN KEY REFERENCES MasterData(ID) ON DELETE CASCADE,       --主数据索引ID  
    3. [SN]               BIGINT CONSTRAINT IX_MDG_Material UNIQUE CLUSTERED IDENTITY(1,1),                                   --自增序列  
    4. [Index]            INT,                                                                                                --序号  
    5. [BarCode]          VARCHAR(16),                                                                                        --条形码  
    6. [Brand]            NVARCHAR(16),                                                                                       --品牌  
    7. [Model]            NVARCHAR(32),                                                                                       --型号  
    8. [Size]             DECIMAL(20,6),                                                                                      --规格  
    9. [SizeType]         VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID),                                                  --规格单位ID,字典  
    10. [Color]            NVARCHAR(8),                                                                                        --颜色  
    11. [Material]         NVARCHAR(8),                                                                                        --材质  
    12. [StorageType]      VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID),                                                  --存储方式ID,字典  
    13. [Description]      NVARCHAR(MAX),                                                                                      --描述  
    14. [Enable]           BIT DEFAULT 1 NOT NULL,                                                                             --是否可用:0、不可用;1、可用  
    15. [CreatorDeptId]    VARCHAR(36) FOREIGN KEY REFERENCES Sys_Organization(ID),                                            --创建部门ID  
    16. [CreatorUserId]    VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,                                           --创建人ID  
    17. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    18. )  
    19. GO  

    3.3    RBAC模型

    3.3.1   角色

    角色表: 

    1. CREATE TABLE Sys_Role(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_Role UNIQUE CLUSTERED IDENTITY(1,1),                                       --自增序列  
    4. [Name]             NVARCHAR(64) NOT NULL,                                                                              --名称  
    5. [Description]      NVARCHAR(MAX),                                                                                      --描述  
    6. [BuiltIn]          BIT DEFAULT 0 NOT NULL,                                                                             --是否预置:0、自定;1、预置  
    7. [CreatorUserId]    VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,                                           --创建人ID  
    8. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    9. )  
    10. GO  

    3.3.2   角色成员

    角色成员(用户)表: 

    1. CREATE TABLE Sys_Role_User(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_Role_User UNIQUE CLUSTERED IDENTITY(1,1),                                  --自增序列  
    4. [RoleId]           VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL,                         --角色ID  
    5. [UserId]           VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,                                           --用户ID  
    6. [CreatorUserId]    VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,                                           --创建人ID  
    7. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    8. )  
    9. GO  

    3.3.3   角色权限

    角色权限表:

    1. CREATE TABLE Sys_RolePerm_Module(  
    2. [ID]               VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),  
    3. [SN]               BIGINT CONSTRAINT IX_Sys_RolePerm_Module UNIQUE CLUSTERED IDENTITY(1,1),                            --自增序列  
    4. [RoleId]           VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL,                         --角色ID  
    5. [ModuleId]         VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL,                       --模块注册ID  
    6. [Action]           INT DEFAULT 0 NOT NULL,                                                                             --操作行为:0、拒绝访问;1、允许访问  
    7. [Mode]             INT DEFAULT 0 NOT NULL,                                                                             --数据授权范围:-1、仅本人;0、仅本部门;1、本部门所有;2、本机构所有;3、全部;4、自定义  
    8. [Permission]       INT DEFAULT 0 NOT NULL,                                                                             --数据权限:0、只读;1、读写  
    9. [CreatorUserId]    VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,                                           --创建人ID  
    10. [CreateTime]       DATETIME DEFAULT GETDATE() NOT NULL                                                                 --创建时间  
    11. )  
    12. GO  

    3.4    如何获取用户的权限

    获取指定用户的功能权限需要先获取该用户可以访问的模块,此功能我们可以使用一个数据库表值函数来返回指定用户被授权访问的模块ID列表。在用户启动某一模块时,使用一个数据库表值函数来返回被授权功能的ID列表。

    在用户访问数据时,我们需要对用户访问的数据根据授权情况进行过滤,并为这些数据加上权限标记,以便告知系统用户被许可的操作方式(只读/读写)。

    我们可以在数据访问层前端进行数据的过滤和标记,这种方式的优点是:

    ²  安全,外部没有注入、篡改的机会。

    ²  高效,数据访问层获取的数据已经经过筛选,不会返回无效的数据。

    ²  兼容性好,和应用系统完全无关,即使应用系统通过存储过程处理数据,也能完全兼容。

    简单地来说,这种机制只需要你在获取数据的时候使用inner join一个表值函数,输入函数的参数(模块ID,登录部门ID,用户ID)即可。

    4      数据库函数

    4.1    功能权限函数

    4.1.1   获取授权模块

    1. /*****表值函数:获取当前登录用户允许访问模块*****/  
    2.    
    3. CREATE FUNCTION Get_PermModule(  
    4. @UserId                VARCHAR(36),    --当前登录用户ID  
    5. @OrgId                 VARCHAR(36)     --当前登录部门ID  
    6. )  
    7.    
    8. RETURNS TABLE AS  
    9.    
    10. RETURN  
    11. with Roles as(  
    12.   select R.RoleId                              --获取当前用户作为成员的角色ID  
    13.     from Sys_Role_User R  
    14.     where R.UserId = @UserId  
    15.   union  
    16.   select R.RoleId                              --获取当前用户所在用户组作为成员的角色ID  
    17.     from Sys_Role_UserGroupR  
    18.     join Sys_UserGroupMemberG on G.GroupId = R.GroupId  
    19.       and G.UserId = @UserId  
    20.   union  
    21.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID  
    22.     from Sys_Role_PositionR  
    23.     join Sys_User_Org P on P.OrgId = R.OrgId  
    24.       and P.UserId = @UserId  
    25.     join Sys_OrganizationO on O.ID = R.OrgId  
    26.       and O.ParentId = @OrgId  
    27.   union  
    28.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID(职位对应部门被合并)  
    29.     from Sys_Role_PositionR  
    30.     join Sys_User_Org P on P.OrgId = R.OrgId  
    31.       and P.UserId = @UserId  
    32.     join Sys_OrganizationO on O.ID = R.OrgId  
    33.     join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId  
    34.       and OM.OrgId = @OrgId  
    35.   )  
    36.    
    37. select M.ModuleId from Roles R  
    38.   join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId  
    39. group by M.ModuleId  
    40. having min(M.Action) > 0  
    41.    
    42. GO  
    4.1.2   获取授权功能

    1. CREATE FUNCTION Get_PermAction(  
    2. @ModuleId              VARCHAR(36),     --模块ID  
    3. @UserId                VARCHAR(36),    --当前登录用户ID  
    4. @OrgId                 VARCHAR(36)     --当前登录部门ID  
    5. )  
    6.    
    7. RETURNS TABLE AS  
    8.    
    9. RETURN  
    10. with Roles as(  
    11.   select R.RoleId                              --获取当前用户作为成员的角色ID  
    12.     from Sys_Role_User R  
    13.     where R.UserId = @UserId  
    14.   union  
    15.   select R.RoleId                              --获取当前用户所在用户组作为成员的角色ID  
    16.     from Sys_Role_UserGroupR  
    17.     join Sys_UserGroupMemberG on G.GroupId = R.GroupId  
    18.       and G.UserId = @UserId  
    19.   union  
    20.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID  
    21.     from Sys_Role_PositionR  
    22.     join Sys_User_Org P on P.OrgId = R.OrgId  
    23.       and P.UserId = @UserId  
    24.     join Sys_OrganizationO on O.ID = R.OrgId  
    25.       and O.ParentId = @OrgId  
    26.   union  
    27.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID(职位对应部门被合并)  
    28.     from Sys_Role_PositionR  
    29.     join Sys_User_Org P on P.OrgId = R.OrgId  
    30.       and P.UserId = @UserId  
    31.     join Sys_OrganizationO on O.ID = R.OrgId  
    32.     join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId  
    33.       and OM.OrgId = @OrgId  
    34.   )  
    35.    
    36. select A.ActionId from Roles R  
    37.   join Sys_RolePerm_ActionA on A.RoleId = R.RoleId  
    38.   join Sys_ModuleActionM on M.ID = A.ActionId  
    39.     and M.ModuleId = @ModuleId  
    40. group by A.ActionId  
    41. having min(A.Action) > 0  
    42.    
    43. GO  

    4.2    数据权限函数

    4.2.1   获取授权数据(部门模式)

    1. CREATE FUNCTION DataPerm_Org(  
    2. @ModuleId              VARCHAR(36),     --模块ID  
    3. @UserId                VARCHAR(36),     --当前登录用户ID  
    4. @OrgId                 VARCHAR(36)      --当前登录部门ID  
    5. )  
    6.    
    7. RETURNS @PermScope  TABLE(  
    8. OrgId                  VARCHAR(36),  
    9. Permission             INT  
    10. AS  
    11.    
    12. BEGIN  
    13. DECLARE @Mode          INT  
    14. DECLARE @Permission    INT  
    15.    
    16. select @Mode = Mode, @Permission = Permission    --获取指定模块对于当前登录用户和登录部门的数据授权访问范围代码和权限代码  
    17. from Get_PermData(@ModuleId, @UserId, @OrgId)  
    18.    
    19. if @Mode = 0  
    20.   insert into @PermScope                         --授权访问范围为仅本部门时,返回本部门(当前登录部门)ID、合并到该部门的部门ID和权限代码  
    21.     select @OrgId, @Permission  
    22.     union  
    23.     select MergerOrgId, @Permission  
    24.     from Sys_OrgMerger  
    25.     where OrgId = @OrgId  
    26.    
    27. else if @Mode between 1 and 3  
    28.   begin  
    29.   if @Mode = 2  
    30.     select @OrgId = dbo.Get_SupOrg(@OrgId, 1)    --获取上级机构ID  
    31.   if @Mode = 3  
    32.     select @OrgId = dbo.Get_SupOrg(@OrgId, 0)    --获取根机构ID  
    33.   insert into @PermScope                         --授权访问范围为本部门及下属、本机构及下属、全部时,返回本部门(当前登录部门)和相应机构/部门ID、合并到上述部门的部门ID和权限代码  
    34.     select ID, @Permission from Get_SubOrg(@OrgId)  
    35.   end  
    36.    
    37. else if @Mode = 4  
    38.   insert into @PermScope                         --自定义授权范围和权限代码  
    39.     select DC.OrgId, max(DC.Permission)  
    40.     from Sys_RolePerm_DataCustomDC  
    41.     join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId  
    42.       and M.ModuleId = @ModuleId  
    43.     join Sys_OrganizationO on O.ID = DC.OrgId  
    44.       and O.Validity = 1  
    45.     group by DC.OrgId  
    46.     union  
    47.     select OM.MergerOrgId, max(DC.Permission)  
    48.     from Sys_RolePerm_DataCustomDC  
    49.     join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId  
    50.       and M.ModuleId = @ModuleId  
    51.     join Sys_OrgMerger OM on OM.OrgId = DC.OrgId  
    52.     group by OM.MergerOrgId  
    53.    
    54. insert into @PermScope                           --无归属部门的数据可被所有人访问  
    55.   select 'All', @Permission  
    56.    
    57. RETURN  
    58. END  
    59. GO  

    4.2.2   获取授权数据(用户模式)

    1. CREATE FUNCTION DataPerm_User(  
    2. @ModuleId              VARCHAR(36),     --模块ID  
    3. @UserId                VARCHAR(36),     --当前登录用户ID  
    4. @OrgId                 VARCHAR(36)      --当前登录部门ID  
    5. )  
    6.    
    7. RETURNS @PermScope  TABLE(  
    8. UserId                 VARCHAR(36),  
    9. Permission             INT  
    10. AS  
    11.    
    12. BEGIN  
    13. DECLARE @Mode          INT  
    14. DECLARE @Permission    INT  
    15.    
    16. select @Mode = Mode, @Permission = Permission    --获取指定模块对于当前登录用户和登录部门的数据授权访问范围代码和权限代码  
    17. from Get_PermData(@ModuleId, @UserId, @OrgId)  
    18.    
    19. if @Mode <0  
    20.   insert into @PermScope  
    21.     select @UserId, @Permission                  --授权访问范围为仅本人时,返回本人ID和权限代码  
    22.    
    23. RETURN  
    24. END  
    25. GO  

    4.2.3   获取数据权限授权模式

    1. CREATE FUNCTION Get_PermData(  
    2. @ModuleId              VARCHAR(36),     --模块ID  
    3. @UserId                VARCHAR(36),     --当前登录用户ID  
    4. @OrgId                 VARCHAR(36)      --当前登录部门ID  
    5. )  
    6.    
    7. RETURNS TABLE AS  
    8.    
    9. RETURN  
    10. with Roles as(  
    11.   select R.RoleId                              --获取当前用户作为成员的角色ID  
    12.     from Sys_Role_User R  
    13.     where R.UserId = @UserId  
    14.   union  
    15.   select R.RoleId                              --获取当前用户所在用户组作为成员的角色ID  
    16.     from Sys_Role_UserGroupR  
    17.     join Sys_UserGroupMemberG on G.GroupId = R.GroupId  
    18.       and G.UserId = @UserId  
    19.   union  
    20.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID  
    21.     from Sys_Role_PositionR  
    22.     join Sys_User_Org P on P.OrgId = R.OrgId  
    23.       and P.UserId = @UserId  
    24.     join Sys_OrganizationO on O.ID = R.OrgId  
    25.       and O.ParentId = @OrgId  
    26.   union  
    27.   select R.RoleId                              --获取当前用户的职位作为成员的角色ID(职位对应部门被合并)  
    28.     from Sys_Role_PositionR  
    29.     join Sys_User_Org P on P.OrgId = R.OrgId  
    30.       and P.UserId = @UserId  
    31.     join Sys_OrganizationO on O.ID = R.OrgId  
    32.     join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId  
    33.       and OM.OrgId = @OrgId  
    34.   )  
    35.    
    36. select max(M.Permission) as Permission, max(M.Mode) as Mode from Roles R  
    37.   join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId  
    38.     and M.ModuleId = @ModuleId  
    39. group by M.ModuleId  
    40.    
    41. GO  

    4.3    其它函数

    4.3.1   获取上级机构ID

    1. CREATE FUNCTION Get_SupOrg(  
    2. @DeptId                VARCHAR(36),    --部门ID  
    3. @Type                  INT            --机构类型:0、根机构;1、上级机构  
    4. )  
    5.    
    6. RETURNS NVARCHAR(36) AS  
    7. BEGIN  
    8.    
    9. DECLARE @NodeType     INT = 0  
    10. DECLARE @ParentId     VARCHAR(36)  
    11.    
    12. while @NodeType !=1  
    13.   begin  
    14.   select @NodeType = NodeType * @Type, @ParentId = ParentId from Sys_Organizationwhere ID = @DeptId  
    15.   if @ParentId is null  
    16.     set @NodeType = 1  
    17.   if @NodeType != 1  
    18.     set @DeptId = @ParentId  
    19.   end  
    20.    
    21. RETURN @DeptId  
    22. END  
    23. GO  

    4.3.2   获取下属机构/部门ID

    1. CREATE FUNCTION Get_SubOrg(  
    2. @OrgId                 VARCHAR(36)      --组织机构ID  
    3. )  
    4.    
    5. RETURNS TABLE AS  
    6.    
    7. RETURN  
    8. with  
    9. OrgList as (  
    10.   select @OrgId as ID  
    11.   union all  
    12.   select O.ID from Sys_OrganizationO  
    13.   join OrgList L on L.ID = O.ParentId  
    14.   where Validity = 1  
    15.     and NodeType < 3),  
    16. MergerOrg as(  
    17.   select OM.MergerOrgId as ID from OrgList OL  
    18.   join Sys_OrgMerger OM on OM.OrgId = OL.ID  
    19.   union all  
    20.   select O.ID from Sys_OrganizationO  
    21.   join MergerOrg M on M.ID = O.ParentId  
    22.   where Validity = 1  
    23.     and NodeType < 3)  
    24.    
    25. select ID from OrgList  
    26. union  
    27. select ID from MergerOrg  
    28.    
    29. GO  

     

    展开全文
  • mybatis-plus统一处理数据权限

    千次阅读 热门讨论 2021-07-14 16:21:42
    问题:项目要求数据权限配置查询,全部、自定义、部门、部门及下级部门、个人。要求做统一处理。 分析: 数据权限精确到个人。那么每张表里面都要有创建人字段。每次插入数据都要保存创建人。查询的时候才能区分。...
  • 数据权限设计:从RBAC到ABAC的演变

    千次阅读 2021-09-28 10:13:57
    本文来说下数据权限设计之从RBAC到ABAC的演变 文章目录概述 概述 名词解释 ACL:Access Control List RBAC:Role-Based Access Control ABAC:Attribute-Based Access Control 权限设计从ACL和RBAC发展而来,...
  • 权限管理-数据权限

    千次阅读 2016-05-12 11:36:56
    权限管理-数据权限
  • erp 数据权限定义(用友NC)

    千次阅读 2015-12-15 18:41:47
    一、数据权限定义 数据权限主要分为维护权限,使用权限,特殊权限。 操作是与业务实体相关联的业务行为,分为维护类操作和使用类操作。 A. 维护类操作:对业务实体数据进行维护,改变其属性的操作,例如删除、修改...
  • 通用数据权限管理系统设计

    千次阅读 2018-03-08 15:33:38
    通用数据权限管理系统设计(一) 作者:逸云 前言: 本文提供一种集成功能权限和数据权限的解决方法,以满足多层次组织中权限管理方面的集中控制。本方法是RBAC(基于角色的访问控制方法)的进一步扩展和延伸,即...
  • 大数据是工业社会的「自由」资源,谁掌握了数据,谁就掌握了主动权。随着企业数字化转型的浪潮,数据更是成为了金融行业的核心资产和创新要素。 而证券行业作为国家金融活动的重要入口,汇聚了大量的金融数据。其...
  • 滴滴大数据安全权限实践

    千次阅读 2020-12-17 21:12:09
    桔妹导读:在滴滴,数据是非常重要的资产,基于数据的数仓建设,数据分析、数据挖掘、数据科学等构建了滴滴的数据体系,支撑着滴滴的业务快速发展。在这个背景下,如何保障用户获取数据的易用性的同时...
  • 如默认只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据,因此不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制为佳,本文主要针对这个特点,对这个数据权限的功能模块...
  • oracle数据字典及用户权限查看

    千次阅读 2017-10-22 20:02:30
    数据字典和动态性能视图 数据字典是数据库中最重要的组成部分,他提供了数据库的一些系统信息。(静态信息,常规的信息)(基表) ...包括基表和数据字典视图。 基表存放数据库的基本信息,普通
  • 数据安全性

    千次阅读 2022-02-21 14:45:09
    1. 数据安全包含哪些方面 1.1 数据备份与恢复 1.2 垃圾回收箱设计 1.3 精细化的权限管理 1.4 操作审计机制 1.5 开发和生产集群物理隔离 1. 数据安全包含哪些方面 大数据方面的数据安全性主要以下三个方面 ...
  • 数据安全分类分级剖析

    千次阅读 2021-09-15 00:04:46
    数据分类分级对于数据的安全管理至关重要,安全分类分级是一个“硬核课题”,从...数据分级通过对不同级别的数据设置相应访问权限、加密规则、脱敏规则等,可大大提升数据安全管控效率,是数据安全精细化管理的重要一步
  • 注意到,在文件权限层面,docker 的用户、用户组是与宿主机相通的,虽然名字可能不一样,但共用一个userid。 也就是说,docker默认的root,id是1,那么对应的就是宿主机的root(userid 也是 1) 如果 docker的默认...
  • RBAC权限模型

    千次阅读 2022-04-27 22:00:06
    包括页面权限、操作权限和数据权限。 页面权限。 页面权限,即用户登录系统可以看到的页面。由菜单控制。菜单包括一级菜单、二级菜单,只有用户有一级菜单、二级菜单的权限,那么用户就可以访问页面。 操作权限。 ...
  • 通用权限管理设计 之 数据权限

    万次阅读 2018-01-04 10:09:40
    前言   前一篇文章《通用权限管理设计 之 数据库设计方案》介绍了【主体】- 【领域】 - 【权限】( who、what、how问题...【数据权限】:能看到哪些数据的问题,如查看本人的所有订单。 【字段权限】:能看到哪
  • drfRBAC 基于Django REST framework的 接口级别 角色访问控制 django-rest-framework RBAC 角色访问控制 接口级别权限 自动权限类生成 自动权限数据生成 自动权限校验 只需着重于权限的分配
  • 本文重点介绍了DSP集成的安全产品(安全技术能力)和差距分析,DSP是以数据安全为核心的保护方案,以数据发现和数据分类分级为基础,混合了多种技术来实现数据安全防护。例如:数据访问控制,数据脱敏,文件加密等,...
  • 数据权限的设计与实现

    万次阅读 2015-12-08 19:25:00
    1.权限分为菜单权限,操作权限,数据权限, 菜单权限即不同用户能够看到的菜单按钮不同,如系统管理员能看到系统管理,用户管理等菜单,而普通用户是看不到这些管理菜单的。 操作权限即为不同用户能够对列表进行的...
  • 1 数据级的权限管理和功能级的权限管理  引自:http://www.iteye.com/problems/97374 功能级权限,有大有小。大的可以直接包括一个业务模块,小的可以是一个按钮。一般的功能级权限一般包括:菜单、url、按钮 。 ...
  • 前期回顾: ⼤数据是如何产⽣的?...元数据具体的工作内容元数据分为技术元数据和业务元数据7 数据治理脏数据的种类数据治理原则知识拓展(数据集市)结束语 数据仓库(数据是如何存储的) 1 什么是数据
  • 1、关于用户权限的几种情况举例: 2、解决思路: 明确一点我们还是要使用@auth.login_required和@auth.verify_password,因为这样可以把接口不公开暴露 我们可以把权限信息写入到token令牌中 我们可以把A、B...
  • 数据,一个简单的定义是描述数据数据。在企业中,无论哪里有数据,都有相应的元...2.数据源元数据描述了数据源的元数据,通常包括四种类型的信息: 数据源地址(例如IP,PORT等) 物理拓扑(例如主备,角色等)
  • PostgreSQL 数据安全之数据加密

    千次阅读 2021-09-01 10:23:17
    PostgreSQL 支持多个不同级别的灵活加密技术,包括密码加密、字段加密、存储加密、传输加密、认证加密以及应用加密,可以保护数据不因数据库服务器被盗、内部管理员或者不安全的网络传输而导致泄露。
  • 数据安全治理方法导论

    千次阅读 多人点赞 2020-11-25 22:30:38
    数据安全-The eye of storm】 目录 第一章 数据安全治理是一套系统工程 1.1 数据安全成为安全的核心问题 1.2 数据泄露路径多元化 1.3 数据安全按相关法律和标准大爆发 1.4 数据安全建设需要有系统化...
  • 角色权限 测试用例

    千次阅读 2019-09-05 14:16:07
    1.一次只给用户单独设置一个权限,设置之后检查该权限是否生效 2.给用户不设置任何权限,设置之后权限检查该用户能否使用系统 3.给用户设置全部权限,设置之后检查所有权限是否生效 4.给用户设置部分权限,设置之后...
  • 非功能性需求都包括哪些方面?

    千次阅读 2019-09-19 10:30:30
    (1) 性能需求:用户在软件响应速度、结果精度、运行时资源消耗量等方面的要求。 (2) 可靠性需求:用户在软件失效的频率、严重程度、易恢复性,以及故障可预测性等方面的要求。 (3) 易用性需求:用户在界面的易用性...
  • 包括图形展示、权限管理、用户管理等功能。 【后端技术】 技术 说明 Spring Boot2 MVC框架 开发的一站式解决方案 Spring Security5  认证和授权框架 MyBatisPlus3.3.1  基于 MyBatis&...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 624,965
精华内容 249,986
热门标签
关键字:

数据权限包括哪些