用图形数据库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,是否很好实现动态角色继承?

原文地址:https://www.cnblogs.com/sweetchildomine/p/8647267.html