# web-RABC-Permissions-sdk

![web-rabc-permissions-sdk](https://s1.ax1x.com/2023/02/06/pSckAHg.png)
<p>
后台管理系统RABC通用权限控制规范与规范代码实施js-SDK，包含 节点/按钮/路由 等控制 ，
<br />
支持hidden,disabled，删除，或则自定义function处理。  通过配置实现RABC的权限控制
</p>

<p align="left">
    <img src="https://www.oscs1024.com/platform/badge/liyuec/easyExcelJs.svg" />
    <img src="https://img.shields.io/badge/size-6.56kb-blue" />
    <img src="https://img.shields.io/badge/license-MIT-orange" />
    <img src="https://img.shields.io/badge/converage-50%25-red" />
    <img src="https://img.shields.io/badge/version-1.0.0-lightgrey" />,
</p>

# 目录
<ul>
  <li><a href="#npm-install">npm install</a></li>
  <li><a href="#基本使用与设计思路">基本使用与设计思路</a></li>
  <li><a href="#具体使用的DEMO地址">具体使用的DEMO地址</a></li>
  <li>
    <a href="#具体配置含义">具体配置含义</a>
    <ul>
    <li><a href="#plan_enum可选">PLAN_ENUM（可选）</a></li>
      <li><a href="#ACTION_ORDER可选">ACTION_ORDER（可选）</a></li>
      <li><a href="#permissionSimpleDTO必选">permissionSimpleDTO（必选）</a></li>
      <li><a href="#webRabcPermissionSdkOptions必选">webRabcPermissionSdkOptions（必选）</a></li>
    </ul>
  </li>
  <li><a href="#启动权限">启动权限</a></li>
<li><a href="#工程架构总览与执行简述">工程架构总览与执行简述</a></li>
  <li><a href="#继续开发计划">继续开发计划</a></li>
</ul>

## npm install[⬆](#目录)<!-- Link generated with jump2header -->
```shell
npm i web-rabc-permissions-sdk -S
```

## 基本使用与设计思路[⬆](#目录)<!-- Link generated with jump2header -->
<b>设计思路：</b>
<ul>
    <li>
        每个web应用，初始的button、容器（div）、任意节点都是可见状态。本实例使用RABC设计思想，实现对web端权限进行控制。
    </li>
    <li>
        正常情况下，用户成功登入系统，只需要配置用户不可使用的节点权限即可。
    </li>
    <li>
       <b>角色配置可以在一个.json里，也可以在一个专用的配置系统里，最佳选择在一个配置系统中，通过ajax读取，这样可以做到随时灵活的设置权限</b>
    </li>
    <li>
        非常规情况补充：
    </li>
    <li>
        <ol>
            <li>用户A与用户B，同时拥有角色1，但是用户A却需要在角色1的基础上拥有更多特性，则可通过用户A拥有多个角色进行补充;
            </li>
            <li>
                用户A与用户B，同时拥有角色1,但是用户A却需要在不增加新角色的情况下，多一些button的控制，则单独设置havePermiss;
            </li>
            <li>
                用户A与用户B，同时拥有角色1,但是用户A 需要某个时间段不拥有某些权限，则设置specialPermiss;
            </li>
            <li>
                某些角色需要使用特定功能，比如使用<b>vue</b>某个 "template"内的方法或则Data，比如使用用户配置的自定义<b>function</b>
            </li>
        </ol>
    </li>
</ul>
<ul>
    <li>
        其中havePermiss 与 noPermiss 会进行 各自的routerPath 与 eleIdOrClass 简单diff判断，进行去重，得到一个以路由为属性的对象，通过当前路由与对象节点匹配，保证执行期间的最小次数。
    </li>
    <li>
        specialPermiss则作为特殊设置，满足特定需求
    </li>
</ul>


```js
 
    //基本使用代码如下

    import {webRabcPermisson,getNewPermissionSimpleDTO,PLAN_ENUM,webRabcPermissionSdkOptions} 
    from 'web-rabc-permissions-sdk';
    /*
        webRabcPermissionSdkOptions     ：基本配置
        PLAN_ENUM                       ：执行计划（程序会自动做降级处理)
        getNewPermissionSimpleDTO       ：每个路由下可能存在的权限
        webRabcPermisson                ：实例类
    */

    let _havePermissArr = [],
      _noPermissArr = [],
      _specialArr = [];



    /*
        假设请求一定成功

        一般情况下，只需要配置用户或则对应角色不可拥有的节点
    */
    ajax('get',url,{...userInfo}).then(res=>{
        let {result} = res;
        //角色 拥有的权限节点
        _havePermissArr = result.map(i=>{
            //得到实体构建节点对象
            let _permissDTO = new getNewPermissionSimpleDTO();
            //该节点的描述，方便debug理解，可空
            _permissDTO.describe = `该节点的描述，方便debug理解，可空`;
            //该节点ID或则className  其中id为  #id;className为 .className
            _permissDTO.eleIdOrClass = `#id`
            //hidden | removeNode   默认hidden，因为removeNode会直接导致DOM结构变更，可能造成副作用，本期暂未实现
            _permissDTO.resultType = 'hidden'
            //具体路由地址，程序执行会根据路由地址匹配，减少执行次数
            _permissDTO.routerPath = '/c/vuepage1'
            return _permissDTO
        })

        //角色 不可拥有节点
        _noPermissArr= result.map(i=>{
            //得到实体构建节点对象
            let _permissDTO = new getNewPermissionSimpleDTO();
            //该节点的描述，方便debug理解，可空
            _permissDTO.describe = `该节点的描述，方便debug理解，可空`;
            //该节点ID或则className  其中id为  #id;className为 .className
            _permissDTO.eleIdOrClass = `.className`
            //hidden | removeNode   默认hidden，因为removeNode会直接导致DOM结构变更，可能造成副作用，本期暂未实现
            _permissDTO.resultType = 'hidden'
            //具体路由地址，程序执行会根据路由地址匹配，减少执行次数
            _permissDTO.routerPath = '/c/vuepage1'
            return _permissDTO
        })

                //角色 不可拥有节点
        _specialArr= result.map(i=>{
            //得到实体构建节点对象
            let _permissDTO = new getNewPermissionSimpleDTO();
            //该节点的描述，方便debug理解，可空
            _permissDTO.describe = `该节点的描述，方便debug理解，可空`;
            //该节点ID或则className  其中id为  #id;className为 .className
            _permissDTO.eleIdOrClass = `.className`
            //hidden | removeNode   默认hidden，因为removeNode会直接导致DOM结构变更，可能造成副作用，本期暂未实现
            _permissDTO.resultType = 'hidden'
            //具体路由地址，程序执行会根据路由地址匹配，减少执行次数
            _permissDTO.routerPath = '/c/vuepage1'
            return _permissDTO
        })

        webRabcPermissionSdkOptions.havePermiss = _havePermissArr;
        webRabcPermissionSdkOptions.noPermiss = _noPermissArr;
        webRabcPermissionSdkOptions.specialPermiss = _specialArr;

        //执行计划  MutationObserver or setTimeout
        webRabcPermissionSdkOptions.plan = PLAN_ENUM.OB_SERVER;
        //对象框架  目前支持获取vue this, react也适用，但是不能获取到某个react组件下的this
        webRabcPermissionSdkOptions.libraryName = 'vue'
        let _webRabc = new webRabcPermisson(webRabcPermissionSdkOptions);

        //启动权限
        _webRabc.start({
        //执行时间
        millisec:500, 
        //obServer的执行节点设置
        obServerConfig:{
                attributes:false,
                childList:true,
                subtree:true,
                characterData:false
            },
        //节流时间
        delay:500,
        //必须指定一个observer的容器节点，必须是ID
        obElem:'app'
        });

    })

    //debug时 获取执行情况
     console.dir(_webRabc.getSdkInfo())

```


## 具体使用的DEMO地址[⬆](#目录)<!-- Link generated with jump2header -->
<a href="https://github.com/liyuec/web-RABC-Permissions-sdk-DEMO">web-RABC-Permissions-sdk 使用DEMO地址</a>


## 具体配置含义[⬆](#目录)<!-- Link generated with jump2header -->

### PLAN_ENUM（可选）[⬆](#目录)<!-- Link generated with jump2header -->

<ul>
    <li>执行计划枚举，在执行计划的时候通过选中方案进行执行。</li>
    <li>如果不支持Mutation方案，则自动降级为setTimeout。</li>
    <li>其中各方案带有节流，以便造成额外的性能开销。</li>
    <li>默认方案为SET_TIMEOUT</li>
</ul>

| 属性名            | 描述 |
| ---------------- | ----------- |
| SET_TIMEOUT          | 通过setTimout实现执行方案 |
| OB_SERVER         | 通过MutationObserver实现执行方案 |


### ACTION_ORDER（可选）[⬆](#目录)<!-- Link generated with jump2header -->

<ul>
    <li>设置当前web应用权限执行顺序逻辑</li>
    <li>默认执行顺序 ---> 当前不可拥有权限 ---> 当前必须拥有权限 ---> 当前特定权限</li>
</ul>

| 属性名            | 描述 |
| ---------------- | ----------- |
| doNoPermiss          | 不可拥有 权限 |
| doHavePermiss         | 可拥有 权限 |
| doSpecialPermiss         | 特殊权限 |


### permissionSimpleDTO（必选）[⬆](#目录)<!-- Link generated with jump2header -->

<ul>
    <li>设置当前web应用权限 havePermiss、noPermiss、specialPermiss 存储索引内容</li>
    <li>每个节点固定DTO描述</li>
</ul>

| 属性名            |是否必选| 描述 |
| ---------------- | ----------- | ----------- |
| routerPath        |必选| 当前路由关键字（react,vue,传统web应用), 也可以直接取"/"，表示所有路由匹配|
| eleIdOrClass      |必选| 当前节点的ID或则ClassName，若是ID，则赋值"#具体ID",若是className，则赋值".具体ClassName"，最终通过querySelector和querySelectorAll获取，建议使用ID |
| resultType        |可选| 默认hidden |
| showElemType     |可选|用户自定义display内容，设置后该节点将变为 display:用户内容!important;若赋值，则不会执行resultType逻辑|
|describe          |可选|节点描述，方便DEBUG查看具体含义，理论上为String类型最大值|
|callBackFunc      |可选|当前节点执行方法;默认不执行，若赋值，则只会执行当前callBackFunc(必须是一个function,箭头函数，class均会被throw)，目前提供具体的4个方法，见如下代码，<b>下个版本计划加入sandBox进行安全信任配置</b>|
|vueTemplateRoot   |可选|当前节点可使用的vue对象，默认为""。若当前节点为正确的vue template ID，则可以在callBackFunc下得到当前vue 的this,通过this调用，若失败或则为""，this则为window|

```js

    //callBackFunc示例
    callBackFunc = function(tools){
        tools.$queryAll('.router_main i').forEach(i =>{
            //你的业务逻辑   
        }
    })

    /*
        其中tools包含
        tools = {
            $getById(el){
                return document.getElementById(el)
            },
            $query(el){
                return document.querySelector(el)
            },
            $queryAll(el){
                return document.querySelectorAll(el)
            },
            $getTagName(el){
                return document.getElementsByTagName(el)
            }
        }

        若libraryName = 'vue' 且 节点vueTemplateRoot 指定为正确的vue template  ，则callBackfunc下的this为当前vue对象，可对当前vue对象做任何操作
    */

```



### webRabcPermissionSdkOptions（必选）[⬆](#目录)<!-- Link generated with jump2header -->

<ul>
    <li>实例构造的最基本DTO</li>
</ul>

| 属性名            |是否必选| 描述 |
| ---------------- | ----------- | ----------- |
| permission        |可选| 为微前端保留的实体结构|
| libraryName        |可选| 本次执行web应用中的基本框架，传入vue 或则 react，其中传入vue ，callBackFunc下获取library下对应的this对象有影响，不传入this默认指向window|
| plan        |必选| 执行方案，参见  PLAN_ENUM |
| havePermiss        |可选|  当前角色下必定显示的节点与路由集合(array)|
| noPermiss        |可选|  当前角色下 需要隐藏或则删除的节点与路由集合(array)|
| specialPermiss        |可选|  当前角色下 特殊的 节点与路由集合(array)|
| actionOrder        |可选|  havePermiss、noPermiss、specialPermiss执行顺序，默认当前不可拥有权限 ---> 当前必须拥有权限 ---> 当前特定权限|



## 启动权限[⬆](#目录)<!-- Link generated with jump2header -->
```js

    let _webRabc = new webRabcPermisson(webRabcPermissionSdkOptions);
      _webRabc.start({
        //PLAN_ENUM.SET_TIMEOUT 的轮询间隔，也可以充当 PLAN_ENUM.OB_SERVER 的节流时间
        millisec:500, 
        //PLAN_ENUM.OB_SERVER 的监控范围
        obServerConfig:{
                attributes:false,
                childList:true,
                subtree:true,
                characterData:false
            },
        //PLAN_ENUM.OB_SERVER 的节流间隔
        delay:500,
        //PLAN_ENUM.OB_SERVER 下具体监控节点
        obElem:'app'
        });

```

## 工程架构总览与执行简述[⬆](#目录)<!-- Link generated with jump2header -->
### 程序执行步骤简述
<ol>
    <li>面向切面的编程思想</li>
    <li>程序会在单例模式的基础上，返回现有实例，保证全局唯一。</li>
    <li>通过内部类constructor 缓存最初传入参数与权限节点</li>
    <li>start() </li>
    <li>
        <ol>
            <li>首先会diff掉 拥有权限与没有权限的数据，解决互斥，减少操作DOM的情况。</li>
            <li>根据routerPath 和 eleIdOrClass 进行权限节点是否相同的判断。</li>
            <li>其次判断传入执行计划，若浏览器不支持，则降级为setTimeout作为执行计划。</li>
            <li>若有function执行黑名单，则会throw错误（<b>后续计划</b>)，用户可自由配置不允许操作的范围，比如cookie操作/localStorage操作等</li>
            <li>最终根据执行计划，匹配当前路由与根据routerPath的关系，执行当前权限。</li>
        </ol>
    </li>
    <li>权限根据执行计划执行</li>
</ol>

![SDK架构图](https://s1.ax1x.com/2022/11/23/z3WOmQ.png)


## 继续开发计划[⬆](#目录)<!-- Link generated with jump2header -->

- [x]   增加sandBox(可选)  
-       黑名单配置
-       运行时验证
-       start前验证
- [ ]   增加微前端模式
- [ ]   可配置执行浏览器空闲执行
- [ ]   resultType增加removeNode逻辑实现
- [ ]   构建aio,es,umd区分


