天天看点

通用接口开放平台设计与实现——(15)消息服务端之数据权限控制

采用发布-订阅者模式,消息中心在消息的生产者和消费者之间充当经纪人角色,负责二者的解耦,对于生产者来说,不需要知道会被哪些消费者消费,对于消费者来说,也不需要知道消息是怎么产生的,由消息中心进行业务逻辑的处理和调度,包括消息的解析、复制、转发等。

对于少数一些业务场景,不需要控制数据权限,例如某一类业务全部交由一个对接系统处理,如做汽运跟踪服务的平台,则只需要将跟踪单据创建和作废消息发到该系统即可,也可能是偏通知类消息,把某类型的消息,同时发送给多个对接系统。

但对于大多数业务场景,是需要进行数据权限控制的,例如将运输需求委托给某个汽运物流服务公司承运,则需要根据承运方进行消息分发,也就说,需要进行数据权限控制,不能将所有的委托单创建消息推送给所有承运商,无论从性能还是数据保密性上来说,都是极不合理的。

整体设计思路如下:

单独建一张数据权限的库表,关键字段属性包括应用标识、数据权限角色编码(如carrier,代表承运商,放到数据字典管理)、数据权限业务编码(如00018938,通常是主数据编码,即承运商对应的编码)

以下几个问题要考虑:

1.1个对接系统通常仅有1个数据权限角色,但不排除有多个的可能,来处理不同的业务,也就是同一应用标识,不同数据权限角色编码,这种情况按多条数据存储

2.同一个系统,同一个数据权限角色情况下,有多个业务编码的情况,如一家承运商,有三家子公司,对接系统是一套,但因为财务结算等原因,主数据中是3个业务编码,这种情况,依旧按多条数据存储。

3.某些业务比较复杂,如业务单据虽然是同一个,但内部进一步区分类型,比如零担和整车运输,需要将消息发送给不同的跟踪服务商,这时候,则需要扩展1个字段,来存放业务类型,这样才能在业务逻辑中进行控制。同理,可以进一步通过增加扩展字段的方式进行三级控制甚至更多,但实际一级就够用,较少会用到二级控制,因此先实现二级控制,后续如有需求,增加字段即可,不影响已实现的功能。

4.某些系统,需要拿到所有数据,如做数据分析的系统,需要拿到所有委托单的数据,同时有各承运商,需要根据编码获取委托给自己的委托单,这时候如果为数据分析系统,增加多行记录既繁琐又不合理,而且在新增承运商的情况下,还需要在数据权限表中为这个数据系统增加1条对应的记录,就很别扭了,这时候,则该使用通配符来优雅处理。

这里举一个具体的例子,将运输需求委托给某个汽运物流服务公司承运,则需要根据承运方进行消息分发,对应的委托单创建的处理器,覆写数据权限过滤方法dataPermissionFilter。

/**
 * 委托单创建请求处理器
 * @author wqliu
 * @date 2022-1-21 19:19
 **/
@Slf4j
public class ConsignmentBillCreateRequestHandler extends RequestMessageHandler {



    @Override
    protected void messageOperation(RequestMessage message, Channel channel) {
        // 获取单号
        String billNo = message.getContent();


    }

    @Override
    protected boolean dataPermissionFilter(RequestMessage message, String appCode) {
        //获取业务单据标识
        String id = message.getContent();
        //通过api调用,获取该业务单据的承运商编码,此处模拟为001
        String carrierCode="001";

        //查找当前应用拥有的承运商数据角色列表
        List<ApiDataPermission> list = apiDataPermissionService.getPermissionByRoleCode(DataRoleEnum.CARRIER.name(), appCode);

        AtomicBoolean hasPermission= new AtomicBoolean(false);
        list.stream().forEach(x->{
            //如数据权限记录的业务编码与单据编码一致,或者使用了通配符,则有权限
            if(x.getBusinessCode().equals(carrierCode) || x.getBusinessCode().equals(DATA_PERMISSION_ALL)){
                hasPermission.set(true);
                return;
            }

        });

        return hasPermission.get();
    }
}
           

继续阅读