用圖形數據庫Neo4j 設計權限模塊

已經 SpringSecurity 在幾個項目中 實現權限模塊,對於數據庫,也是思考了很多,從Mysql 到 mongodb 都不是特別滿意,node

在Mysql中,若是權限相對簡單,那麼還能接受,若是稍微複雜一點,那麼就有點噁心了.web

在最近一個項目中,使用mongodb 作多租戶的權限,實現起來簡單明瞭了不少,關係也沒有那麼繞,可是畢竟非關係型數據庫,沒有級聯操做,修改刪除,可能會留下一些髒數據,spring

雖然Spring Data Mongodb 有對象持久化的監聽事件,可是依然須要手動編寫一些處理過時,以及髒數據的代碼.sql

最近發現有個東西叫作Neo4j,好說了,這個特別關係的數據庫,第一個想法,就是很適合作這種關係複雜的權限模塊.mongodb

這裏模擬一個基於多租戶的權限設計數據庫

 

1:租戶依賴系統權限,根據租戶付費套餐不一,擁有不同的權限,但不可越過系統權限邊界(廢話)api

2:租戶能夠建立角色(角色不能夠越過租戶權限的邊界)app

3:租戶建立的用戶,能夠有多個角色(權限基於租戶的權限,多角色疊加)spa

 

首先用kubernetes 啓動一個neo4j設計

neo4j.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: neo4j
  namespace: k8s-springcloud
spec:
  replicas: 1
  selector:
    matchLabels:
      app: neo4j
  template:
    metadata:
      labels:
        app: neo4j
    spec:
      nodeName: k8s-node-0
      terminationGracePeriodSeconds: 60
      hostNetwork: true
      containers:
      - name: neo4j
        image: 192.168.91.137:5000/neo4j
        volumeMounts:
        - name: data
          mountPath: /data
        - name: conf
          mountPath: /var/lib/neo4j/conf
      volumes:
      - name: data
        hostPath:
          path: /mnt/gv0/k8s-springcloud/neo4j/data
      - name: conf
        hostPath:
          path: /mnt/gv0/k8s-springcloud/neo4j/conf


---

apiVersion: v1
kind: Service
metadata:
  name: neo4j
  namespace: k8s-springcloud
  labels:
    app: neo4j
spec:
  type: NodePort
  ports:
  - name: api
    port: 7687
    nodePort: 7687
    targetPort: 7687
  - name: web
    port: 7474
    nodePort: 7474
    targetPort: 7474

kubectl create -f neo4j.yaml

 

首先看看系統總權限(假設系統有兩個模塊:訂單(增刪改查),庫存(增刪改查))

代碼:

        AclTenantModuleRelation sysModuleRelation = new AclTenantModuleRelation();

        AclModule orderModule = new AclModule();
        orderModule.setCode("AUTH_ORDER");
        orderModule.setName("訂單管理");

        AclMethod orderQuery = new AclMethod("AUTH_ORDER_QUERY", "查詢訂單");
        orderQuery.setAclModule(orderModule);
        AclMethod orderDelete = new AclMethod("AUTH_ORDER_DELETE", "刪除訂單");
        orderDelete.setAclModule(orderModule);
        AclMethod orderEdit = new AclMethod("AUTH_ORDER_EDIT", "編輯訂單");
        orderEdit.setAclModule(orderModule);
        AclMethod orderAdd = new AclMethod("AUTH_ORDER_ADD", "新增訂單");
        orderAdd.setAclModule(orderModule);

        orderModule.setAclMethods(new HashSet<>(Arrays.asList(orderAdd, orderDelete, orderEdit, orderQuery)));

        aclMethodService.saveAll(orderModule.getAclMethods());

        aclModuleService.save(orderModule);

        sysModuleRelation.setTenantId(TenantInfo.SYSTEM_TENANT_ID);

        AclModule stockModule = new AclModule();
        stockModule.setCode("AUTH_STOCK");
        stockModule.setName("庫存管理");

        AclMethod stockQuery = new AclMethod("AUTH_STOCK_QUERY", "查詢庫存");
        stockQuery.setAclModule(stockModule);
        AclMethod stockDelete = new AclMethod("AUTH_STOCK_DELETE", "刪除庫存");
        stockDelete.setAclModule(stockModule);
        AclMethod stockEdit = new AclMethod("AUTH_STOCK_EDIT", "編輯庫存");
        stockEdit.setAclModule(stockModule);
        AclMethod stockAdd = new AclMethod("AUTH_STOCK_ADD", "新增庫存");
        stockAdd.setAclModule(stockModule);

        stockModule.setAclMethods(new HashSet<>(Arrays.asList(stockAdd, stockDelete, stockEdit, stockQuery)));

        aclMethodService.saveAll(stockModule.getAclMethods());

        aclModuleService.save(stockModule);

        sysModuleRelation.setAclModules(new HashSet<>(Arrays.asList(orderModule,stockModule)));

        sysModuleRelation.setAclMethods(new HashSet<>(Arrays.asList(orderAdd, orderDelete, orderEdit, orderQuery,stockAdd, stockDelete, stockEdit, stockQuery)));

來設置一個租戶,給租戶分配一些權限

    /**
     * User: laizhenwei
     * Date: 2018-03-25 Time: 15:09
     */
    @Test
    public void initTenantModule(){
        AclModule aclModule = aclModuleService.findTop1ByCode("AUTH_STOCK");
        TenantInfo tenantInfo = new TenantInfo("租戶1");
        tenantInfoService.saveAndFlush(tenantInfo);
        Iterator<AclMethod> aclMethodIterator = aclModule.getAclMethods().iterator();
        AclTenantModuleRelation aclTenantModuleRelation = new AclTenantModuleRelation(tenantInfo.getId(),new HashSet<>(Arrays.asList(aclModule)),new HashSet<>(Arrays.asList(aclMethodIterator.next(),aclMethodIterator.next())));
        aclTenantModuleRelationService.save(aclTenantModuleRelation);
    }

CQL

match (tenantModule:AclTenantModuleRelation)-[:HAS_OF]->(atMethod:AclMethod) where tenantModule.tenantId = '40288183625d600c01625d6032fa0000' match (atMethod)-[:DEPEND_OF]->(module:AclModule) return atMethod,module

 

模擬租戶建立一個角色

   /**
     * User: laizhenwei
     * Date: 2018-03-25 Time: 15:09
     */
    @Test
    public void addRole(){
        Optional<AclTenantModuleRelation> aclTenantModuleRelationOptional = aclTenantModuleRelationService.findById(212l);
        aclTenantModuleRelationOptional.ifPresent(aclTenantModuleRelation -> {
            AclRole aclRole = new AclRole();
            aclRole.setTenantId("40288183625d600c01625d6032fa0000");
            aclRole.setCode("ROLE_NORMAL");
            aclRole.setName("普通用戶");
            aclRole.setAclTenantModuleRelation(aclTenantModuleRelation);
            Iterator<AclModule> aclModuleIterator = aclTenantModuleRelation.getAclModules().iterator();
            AclModule aclModule = aclModuleIterator.next();
            Iterator<AclMethod> aclMethodIterator = aclTenantModuleRelation.getAclMethods().iterator();
            aclRole.setAclModules(new HashSet<>(Arrays.asList(aclModule)));
            aclRole.setAclMethods(new HashSet<>(Arrays.asList(aclMethodIterator.next())));
            aclRoleService.save(aclRole);
        });
    }

CQL

match (t:AclTenantModuleRelation)-[:HAS_OF]->(method:AclMethod) match(r:AclRole)-[:HAS_OF]->(rmethod:AclMethod) where r.tenantId='40288183625d600c01625d6032fa0000' and r.code='ROLE_NORMAL' and method=rmethod match(method)-[:DEPEND_OF]->(module:AclModule) return method,module

結果(左),點擊展開關係(右)

 

用戶與角色,再也不作演示.

想一想SpringSecurity 的角色繼承的配置格式爲  role1>role2>role3 這種方式,用neo4j,是否很好實現動態角色繼承?

相關文章
相關標籤/搜索