• 用图形数据库Neo4j 设计权限模块


    已经 SpringSecurity 在几个项目中 实现权限模块,对于数据库,也是思考了不少,从Mysql 到 mongodb 都不是特别满意,

    在Mysql中,如果权限相对简单,那么还能接受,如果稍微复杂一点,那么就有点恶心了.

    在最近一个项目中,使用mongodb 做多租户的权限,实现起来简单明了了很多,关系也没有那么绕,但是毕竟非关系型数据库,没有级联操作,修改删除,可能会留下一些脏数据,

    虽然Spring Data Mongodb 有对象持久化的监听事件,但是依然需要手动编写一些处理过期,以及脏数据的代码.

    最近发现有个东西叫做Neo4j,好说了,这个特别关系的数据库,第一个想法,就是很适合做这种关系复杂的权限模块.

    这里模拟一个基于多租户的权限设计

    1:租户依赖系统权限,根据租户付费套餐不一,拥有不一样的权限,但不可越过系统权限边界(废话)

    2:租户可以创建角色(角色不可以越过租户权限的边界)

    3:租户创建的用户,可以有多个角色(权限基于租户的权限,多角色叠加)

    首先用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,是否很好实现动态角色继承?

  • 相关阅读:
    jquery键盘事件
    如何将奇艺、优酷等平台的视频嵌入到项目中
    ubuntu 10.04 常用 设置
    博客风格收集
    多张图片上传预览
    动态计算输入框字符个数
    Ubuntu Linux下设置IP的配置命令
    js事件浏览器兼容
    开源软件下载站
    PHPstrom的一个小技巧
  • 原文地址:https://www.cnblogs.com/sweetchildomine/p/8647267.html
Copyright © 2020-2023  润新知