0.前言
前段时间忙了其余事了,感受利用周末的时间效率好低哦。没有平时上班时间的效率高。哈哈哈。这篇博客,主要是物联网业务服务器前期的一些简单设计。主要是设备如何进行登陆,从业务服务器那里获取Token后,登录到MQTT服务器。业务服务器对设备的登陆验证,ACL权限验证这两方面。主要是把业务服务器与MQTT服务器联系起来。javascript
距离上次EMQ学习已经有一段时间了,测试服务器也已经换了。因为一些缘由,只能用本身的服务器了。博客中出现的IP或者域名,因为只是测试环境,没有进行过多的限制,有幸看到博客的,请不要搞破坏哦。哈哈哈。/滑稽html
每一个人对业务的设计的不一样的,我只是根据本身的了解来设计的。有更好的想法,能够在下面评论区,跟我讨论哦。java
1.EMQ服务器配置
本次使用2.3.5版本。直接是从github.com上拉取下来的,而后进行源代码进行安装的。前期开发不用涉及到插件开发,能够直接使用官网提供的二进制包。否则的话,就用源代码本身编译。注意这里若是本身编译的话,Erlang要 R20+版本才能够。本身到http://www.erlang.org 下载新版的Erlang/OTP。node
一切的编译步骤,我在前面的博客有提到,不清楚的能够看我前面的博客。虽然EMQ进行了小版本更新,可是大致的编译流程仍是差很少了。这里就很少说了。jquery
编译后,会在emq-relx目录下生成_rel/emqttd目录。git
我以为MQTT通讯服务器与业务服务器是要尽可能分开的,特别是不要在MQTT服务器里作过多的业务处理。因此使用EMQ服务器里面的账号验证和ACL权限验证。采用的是Redis中间件验证方式。github
因为默认的EMQ服务器是很少账号密码进行验证和主题的验证。这里咱们要开启验证。下面的这些配置是对默认的配置进行的修改。ajax
(1)data/loaded_plugins 文件redis
这里要增长一行emq_auth_redis算法
(2)etc/emq.conf
因为如今是单服务器,性能配置什么都是默认的就能够。如今我尚未了解全部的配置,性能调优之后看有没有机会了解。
这里要修改的是能够修改node.name 和 node.cookie,两个也可使用默认就能够。
因为默认的EMQ服务器是不检查账号密码和ACL权限的。这里须要修改emq.conf使其要进行验证。还有cache_acl这个默认是true, 测试的时候最好是设置为false,这里若是是true的话,那么EMQ一样会对ACL进行验证。可是只验证一次,就是在第一次发布或者订阅对应的Topic时会去判断ACL,若是经过了,那么下次对这个Topic进行sub/pub是再也不进行判断验证的,同理不经过了,下次也不会进行验证。若是有cache是能够提升性能,可是对于那些须要对权限动态修改的业务场景,这个功能就不能用了。对应的配置项是
1 mqtt.allow_anonymous = false 2 mqtt.acl_nomatch = deny 3 mqtt.cache_acl = false
(3)etc/plugins/emq_auth_redis.conf
修改对应的redis地址和密码, 分别对应的配置项是 auth.redis.server=127.0.0.1:6379 和 auth.redis.password=password 剩下的cmd不用修改,使用默认的,后续开发若是须要自定义的,能够修改这里的cmd。
嗯,剩下的就不用再配置了。
./bin/emqttd start 启动EMQ服务器就能够。而后经过Web管理界面进行查看,服务是否启动。而后在里面提供的WebSocket界面测试链接。
2.业务代码
若是在EMQ服务器里作业务处理的话,不是不行,可是呢,Erlang这个语言,处理业务很差,我也不熟悉,做为一个项目来讲,招人也很差招。
我仍是使用较为通用的Java来作业务处理。Java的生态会好不少。本次使用Spring Boot 2.0框架。因为本篇博客主要讲的是物联网业务方面的,关于Spring Boot的一些配置,这里就不展开了。之后有机会再写成博客说明。
从上面的图能够看到,如今没有账号密码是不能登陆到MQTT服务器了。EMQ是经过查询Redis而后进行验证的。因此我只须要在业务服务器增长一段往Redis服务器写入权限控制的数据记录便可。关于默认的格式,参考EMQ文档。
Login代码段
1 @Autowired 2 private StringRedisTemplate stringredisTemplate; 3 4 @RequestMapping(value="login") 5 public @ResponseBody IOTDeviceModel login(HttpSession session, 6 @RequestParam("username") String username, @RequestParam("password") String password){ 7 //检验账号密码 8 IOTDeviceModel dev = new IOTDeviceModel(); 9 if(username.equals("demo")){ 10 dev = getIOTDeviceModelDemo(); 11 }else if(username.equals("test")){ 12 dev = getIOTDeviceModelTest(); 13 }else{ 14 dev.setMsg("用户名,密码错误."); 15 return dev; 16 } 17 session.setAttribute("session_user", dev); 18 //设置到 mqtt-redis 并返回token 19 stringredisTemplate.opsForHash().put("mqtt_user:" + dev.getUUID(), "password", dev.getToken()); 20 stringredisTemplate.expire("mqtt_user:" + dev.getUUID(), 100, TimeUnit.SECONDS); 21 //这里查询模拟数据数据库,容许当前用户能够发布和订阅的Topic 22 stringredisTemplate.opsForHash().put("mqtt_acl:" + dev.getUUID(), "/publicroom", "3"); 23 //返回成功 24 dev.setMsg("建立成功, 请使用uuid,token 登录到mqtt服务器"); 25 return dev; 26 }
GetModel代码段
1 //测试用户 Demo 2 private IOTDeviceModel getIOTDeviceModelDemo(){ 3 IOTDeviceModel model = new IOTDeviceModel(); 4 model.setDevid(1); 5 model.setUsername("demo"); 6 model.setPassword("demo"); 7 model.setUUID("5a53a33d-98af-4f87-bb57-cc2e21450b36"); 8 model.setToken(UUID.randomUUID().toString()); //生成临时的Token 9 return model; 10 } 11 //测试用户Test 12 private IOTDeviceModel getIOTDeviceModelTest(){ 13 IOTDeviceModel model = new IOTDeviceModel(); 14 model.setDevid(2); 15 model.setUsername("test"); 16 model.setPassword("test"); 17 model.setUUID("f9a06e81-3f12-425b-b58d-21fca17b9932"); 18 model.setToken(UUID.randomUUID().toString()); 19 return model; 20 }
上面的代码很简单了,就是判断当前账号密码是否正确,若是正确的,就写入Redis。可能会有疑问,为何设置到Redis的密码,不直接使用password字段,而是随机生成Token。这是因为业务中数据库保存的密码通常都是加密后的。而加密算法也是各不相同,默认的MD5可能不知足,固然也有基于一些其余的缘由。这里就是返回Token,而后用户、设备经过业务服务器获取到Token,登陆到MQTT服务器上。
这样就登陆成功了。如今这个账号只能发布和订阅/publicroom这个主题。经过自带的测试工具进行测试以下
能够发现,分别对/World 和 /publicroom这两个主题进行订阅和发布,都是成功的,可是真正在服务器发送数据通讯的只有/publicroom这个主题,由于只有这个主题是被容许的,/World这个主题不容许。
其实按照正常逻辑来讲,这里的/World最好是连订阅都是不容许的,可是好像默认的这里WebSocket插件没有作处理。若是之后我基于本身开发插件的话,这里能够进行限制处理了。
3.测试结果
主要从新梳理一下流程。
(1)一个简答的测试界面
1 <!DOCTYPE html> 2 <html xmlns:th="http://www.thymeleaf.org" 3 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> 4 <head> 5 <meta name="viewport" content="width=device-width,initial-scale=1"/> 6 <meta charset="UTF-8"/> 7 <title>EMQ-测试</title> 8 </head> 9 <body> 10 如下是模拟用户/设备登陆业务服务获取Token<br> 11 用户名: <input id="username" /> 12 密码: <input id="password" /> 13 <button id="login-btn">登陆业务服务器</button> <br> 14 登录后信息: <div id='login-ret'></div> 15 16 <hr> 17 如下是用户/设备获取Token后登陆到MQTT服务器<br> 18 用户名: <input id="mqtt-username"/> 19 密码: <input id="mqtt-password"/> 20 <button id="mqtt-login-btn">登陆MQTT</button> <br> 21 登录后信息: <div id="mqtt-login-ret"></div> 22 23 <hr> 24 登陆到MQTT后 25 <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" type="text/javascript"></script> 26 <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script> 27 <script th:inline="javascript"> 28 jQuery(function($){ 29 bindBtnLogin(); 30 }); 31 32 function bindBtnLogin(){ 33 $("#login-btn").bind('click', function(){ 34 var username = $("#username").val(); 35 var password = $("#password").val(); 36 $.post("/login", { 37 username: username, 38 password: password 39 }, function(ret){ 40 console.log(ret); 41 $("#login-ret").html(JSON.stringify(ret)); 42 }); 43 }); 44 } 45 </script> 46 </body> 47 </html>
输入demo demo 进行登陆,而后返回Token,而后我使用MQTT客户端去链接。这里的客户端使用的是 Eclipse Paho MQTT Utility 工具。
填写对应的服务器地址,而后在选项里写入账号密码,这里的账号就是上图的UUID,密码就是上图的Token了。而后登陆成功。
(2)查看Redis数据
这里使用Redis客户端进行查看,方便调试
这里的mqtt_user:** 因为设置了TTL,因此会在一段时间后自动删除掉。
(3)客户端订阅主题
依次在下面订阅/publicroom和/TTT两个主题,会发现两个都订阅成功(具体缘由,上面有说到)。而后在下面的发布,分别对/publicroom和/TTT主题进行发布信息。下图是运行结果。注意这里的订阅要分开进行订阅,不要两个选中而后订阅。这里的/TTT订阅不成功了?这么说,EMQ服务器应该是有处理返回的。
(4)再用另外的账号
这里同理,按照上面的流程,再用另外的账号,进行登陆,而后两个账号互发信息。经测试,基本符合预期的。
(5)Web管理界面
这里看一下几个管理界面。
两个用户,ClientID分别就是上面业务服务器模拟的两台设备。
4.简单流程图
5.其余
最后多说两句,一个产品的业务逻辑和业务需求的前期确认是跟一开始的Topic设计是相关的。Topic若是一开始设计的很差,后面的业务就很难进行扩展了。只能往EMQ增长逻辑判断,我以为是很没有必要的。不少能够靠Topic的巧妙设计来避开复杂的业务逻辑。
另外,须要不少对设备的统计,操做的处理.例如统计当前主题下订阅的用户,踢出主题下的某个用户,系统推送下发消息等.好比统计用户,这个我以为是业务相关的,须要从业务服务器中获取,可是业务服务器通常没有这些数据,因此就须要业务服务器Hook到MQTT服务获取实时在线数.又好比消息下发推送,这个确定是业务服务器下发消息,好比广播等,一样要写MQTT插件.
固然,这个是我的的想法。期待下一篇博客 业务系统设计2。