从零开始搭建物联网云平台03-MQTT消息处理设计

从零开始搭建物联网云平台03-MQTT消息处理设计

在IoTechn项目中,大致可分为四个部分 1.用户终端 2.设备终端 3.设备网关(MQTT Broker) 4.应用服务器。小程序、App等用户终端主要通过HTTP和WebSocket通信,这里不做讨论。

权限鉴定

对暴露在公网的服务,都需要进行权限鉴定,作为设备网关的MQTT Broker也不例外。IoTechn使用的Broker为EMQX。EMQX提供了多种鉴权插件,包括数据库,缓存等鉴权。但是为了能够灵活鉴权,我们采用的是HTTP鉴权。

设备权限鉴定

鉴权方式确定后,剩下的就是鉴权的业务逻辑了。鉴权业务可分为两类

1.连接许可鉴权。
@RequestMapping("/auth")
public ResponseEntity<?> doAuth(String clientid, String ip, String username, String password) {
    if (this.whiteIps.contains(ip)) {
        return ResponseEntity.ok("ignore");
    }
    if (!clientid.equals(username)) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Forbidden!");
    }
    try {
        deviceBizService.auth(ip, username, password);
        logger.info("[设备连接] accessKeyId=" + username);
        return ResponseEntity.ok("ignore");
    } catch (ServiceException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Forbidden!");
    }
}

当设备连接来自于IP白名单时,则直接鉴权成功,此白名单主要配置内网环境中的应用服务器。若不是白名单,则直接将鉴权工作交给DeviceBizService进行权限鉴定。

@Override
@DS(Const.DB_S)
public String auth(String ip, String username, String password) throws ServiceException {
    DeviceDO deviceDO = deviceMapper.selectOne(new QueryWrapper<DeviceDO>()
            .select("id", "access_key_id", "access_key_secret", "auth_type")
            .eq("access_key_id", username));
    if (deviceDO == null) {
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_DEVICE_NOT_EXIST);
    }
    // 进行权限鉴定
    if (deviceDO.getAuthType().intValue() == DeviceAuthType.EASY.getCode()) {
        if (!deviceDO.getAccessKeySecret().equals(password)) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PERMISSION_DENY);
        }
    } else if (deviceDO.getAuthType().intValue() == DeviceAuthType.SAFE.getCode()) {
        if (!MD5Util.md5(ip + deviceDO.getAccessKeySecret()).equalsIgnoreCase(password)) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PERMISSION_DENY);
        }
    } else {
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_UNKNOWN_EXCEPTION);
    }
    return "ok";
}

设备鉴权鉴权密码分为两个模式,

1.简单模式,直接将AccessKeySecret作为鉴权密码,这样可降低接入门槛。

2.MD5模式,将MD5(IP + AccessKeySecret)作为鉴权密码,MD5模式的好处就是,即使被截获,若是没有黑到到该IP的跳板机,也无法鉴权成功。

2.Topic读写权限。
@RequestMapping("/acl")
public ResponseEntity<?> doAcl(String clientid, String username, String ip, String topic, Integer access) throws IOException {
    if (this.whiteIps.contains(ip)) {
        return ResponseEntity.ok("ignore");
    }
    if (topic.equals(DeviceConst.TOPIC_UP + username)) {
        return ResponseEntity.ok("ignore");
    }
    if (topic.equals(DeviceConst.TOPIC_DOWN + username) && access == 1) {
        // 命令TOPIC只允许白名单IP(即内网写)
        return ResponseEntity.ok("ignore");
    }
    return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Forbidden!");
}

一个设备可对两个TOPIC进行访问,一个下行TOPIC一个上行

消息处理

欲使服务器和硬件之间能够完成消息传递,大致可分为这几个步骤 1.构建消息体 2.消息序列化 3.网络流传输 4.消息反序列化 5.消息处理

以上行消息为例,1&2 是在设备上完成 3 属于二者共同完成 4&5 则是在服务器上完成。反之亦然。

对于服务器而言第一步要做的事就是消息反序列化, 所以定义了一个反序列化结构

public interface IotMqttDeserializer {
    public <T> T parseObject(byte[] payload, Class<T> clazz);
    public <T> List<T> parseArray(byte[] payload, Class<T> clazz);
}

第二部则是需要对消息进行处理,所以定义了消息处理接口

public interface IotMqttMessageHandler<T> {
    public int handle(String accessKeyId, T msg);
    public int getCode();
    public Class<T> getMessageType();
}

IotMqttMessageHandler中定义了三个方法

1.handle 处理器核心方法 ,用于处理消息

2.getCode 获取消息类型Code,某一个Handler只能处理特定Code的消息。

3.getMessageType 获取反序列化结果的Java Class对象,主要用于反序列化

问:如何通过设备的消息找到具体的处理器?

答:通过消息Code 和 消息处理Handler映射来Hash表来找,所以你发现getCode有什么用了。

@Bean
public Map<Integer, IotMqttMessageHandler> messageHandleRouter(List<IotMqttMessageHandler> iotMqttConnectMessageHandlerList) {
    return iotMqttConnectMessageHandlerList.stream().collect(Collectors.toMap(IotMqttMessageHandler::getCode, v -> v));
}

消息下行

通过MQTTClient for Java往设备下行Topic发送即可

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值