1.      mosquitto的插件的接口在文件mosquitto_plugin.h中进行了声明,在创建自己的鉴权插件代码中必须包含头文件mosquitto_plugin.h,并且实现该头文件中声明的所有鉴权接口函数(接口函数mosquitto_log_printf除外)。

2.      mosquitto插件的权限校验方式由头文件mosquitto_plugin.h中声明的接口函数所限制,共提供用户名密码校验、acl列表校验、psk_key校验。

3.      动态库相关的操作宏,在mosquitto代码中的lib_load.h文件中,为了支持跨平台,对动态库的加载、关闭、获取函数进行了统一的宏定义:

#ifdef WIN32

#        defineLIB_LOAD(A) LoadLibrary(A)

#        defineLIB_CLOSE(A) FreeLibrary(A)

#        defineLIB_SYM(HANDLE, SYM) GetProcAddress(HANDLE, SYM)

#else

#        defineLIB_LOAD(A) dlopen(A, RTLD_NOW|RTLD_GLOBAL)

#        defineLIB_CLOSE(A) dlclose(A)

#        defineLIB_SYM(HANDLE, SYM) dlsym(HANDLE, SYM)

#endif

这里统一定义了动态库加载宏:LIB_LOAD,动态库关闭的宏LIB_CLOSE,以及获取动态库中对外导出函数的宏LIB_SYM。

4.      插件相关的参数配置

Mosquito的配置文件mosquitto.conf中为插件的参数做了专门说明:如果要使用插件,则必须启用配置参数auth_plugin,并通过此参数指定插件动态库的位置,在配置文件中可以仿照如下配置:

auth_plugin /usr/lib/auth-plug.so

如果插件中需要用到配置参数,则需要全部以auth_opt_*作为前缀,例如我的插件要redis,那么他们的参数就可以在配置文件中这样配置插件所需要的redis参数:

auth_opt_redis_host 192.168.2.154

auth_opt_redis_port 6379

auth_opt_redis_pass 123456

在mosquitto加载配置的时候,会将这些配置参数(以auth_opt_为前缀的参数)加载到struct mqtt3_config 结构体变量的auth_options成员中,auth_options被定义成struct mosquitto_auth_opt类型的数组,通过structmqtt3_config 结构体变量另一个成员auth_option_count来记录数组的长度,而结构体struct mosquitto_auth_opt就是一个key-value结构,如下所示:

structmosquitto_auth_opt {

char *key;

char *value;

};

如此以来,在程序启动的时候,所有插件需要的配置参数都会被加载到程序内存,并且在插件的初始化时(对应插件的接口函数mosquitto_auth_plugin_init)将配置文件数组auth_options以及数组长度auth_option_count传递给插件。

【注意】 mosquitto在传递配置参数名和值给插件时,不传递参数名前缀auth_opt_,例如我们在配置文件mosquitto.conf中配置了参数auth_opt_redis_port 6379,我们在初始化插件时拿到的key实际上是redis_host,而前缀auth_opt_则不会被传入到插件中。

5.      Mosquito对插件的使用方式

Mosquito在main函数启动的时候会调用封装的函数mosquitto_security_module_init来初始化插件,在mosquitto退出的时候会执行mosquitto_security_module_cleanup函数,在该函数中会调用插件的资源释放函数mosquitto_auth_plugin_cleanup来释放插件中申请的资源,例如连接之类。

在mosquitto代码中,对插件的加载、检测操作主要在文件security.c中的函数mosquitto_security_module_init里进行,它主要做了如下事情:

(1)      判断配置参数auth_plugin中是否提供了插件的全名,如果是,则通过宏LIB_LOAD加载插件的动态库;

(2)      加载动态库之后,立即调用插件接口函数mosquitto_auth_plugin_version获取插件的版本,并对插件版本进行检测,看其值是否为:MOSQ_AUTH_PLUGIN_VERSION,如果不是,则关闭动态库并返回失败;

(3)      检测接口函数是否实现,通过读取插件动态库里面的mosquitto_auth_plugin_init函数、mosquitto_auth_plugin_cleanup函数、mosquitto_auth_security_init函数、mosquitto_auth_security_cleanupt函数、mosquitto_auth_acl_check函数、mosquitto_auth_unpwd_check函数、mosquitto_auth_psk_key_get函数地址,并将这些函数的地址放在结构体成员db->auth_plugin的各函数指针中,如果没有找到这写函数中的任何一个则关闭动态库,返回失败;

(4)      初始化动态库,通过调用db->auth_plugin.plugin_init,也就是第(3)步mosquitto_auth_plugin_init对插件动态库进行初始化操作;

(5)      Mosquito在文件security.c中对插件所提供的全部进行了封装,例如:mosquitto_security_cleanup函数封装了插件提供mosquitto_auth_plugin_cleanup函数、mosquitto_security_init函数对插件的mosquitto_auth_security_init函数进行了封装;mosquitto_security_cleanup函数对插件中提供的mosquitto_auth_security_cleanup函数进行了封装、函数mosquitto_acl_check对插件提供的mosquitto_auth_acl_check函数进行了封装。。。。。。

6.      插件数据交互

Mosquitto关于插件的相关结构体的定义如下(此结构体的定义在文件mosquitto_broker.h中):

struct _mosquitto_auth_plugin{

void*lib;

void*user_data;

int(*plugin_version)(void);

int(*plugin_init)(void **user_data, struct mosquitto_auth_opt *auth_opts, intauth_opt_count);

int(*plugin_cleanup)(void *user_data, struct mosquitto_auth_opt *auth_opts, intauth_opt_count);

int(*security_init)(void *user_data, struct mosquitto_auth_opt *auth_opts, intauth_opt_count, bool reload);

int(*security_cleanup)(void *user_data, struct mosquitto_auth_opt *auth_opts, intauth_opt_count, bool reload);

int(*acl_check)(void *user_data, const char *clientid, const char *username, constchar *topic, int access);

int(*unpwd_check)(void *user_data, const char *username, const char *password);

int(*psk_key_get)(void *user_data, const char *hint, const char *identity, char*key, int max_key_len);

};

需要关注的是指针void*user_data,该用于在插件的各接口之间传递数据,在开发自己的mosquitto插件的时候,需要在实现插件的初始化接口函数mosquitto_auth_plugin_init的代码中中创建一个自己的内存空间(例如,可以创建自己的结构体),然后将该结构体的首地址传送给mosquitto,在后续的每个鉴权函数的接口中的第一个参数就是这个地址,如下以用户名和密码校验举例子更容易说明问题:

假如我们的一些信息存储到了数据库,在鉴权插件中,我们需要从数据库中读取用户名和密码,然后对客户端传递过来的用户名和密码进行校验,这时,我们可以在插件初始化的时候创建和数据库的连接,然后将连接信息放在自定义的结构体中,把这个结构体通过插件初始化函数mosquitto_auth_plugin_init的参数void**user_data传递给mosquitto,后续校验函数mosquitto_auth_unpwd_check中,只需要从该结构体地址中获取到连接信息,就可以直接从数据库中读取数据了,而不用每次校验都要跟数据库建立连接。

7.      插件接口函数说明:

(1)      mosquitto_log_printf,工具类接口,即你可以定义自己的日志输出函数来代替mosquitto代码里的日志输出;这个接口函数不是必须实现的。

(2)      接口函数mosquitto_auth_plugin_version

mosquitto在加载插件之后,将立即调用此接口函数来检测自己是否支持该版本的插件,在自己实现改接口函数时只需(也是必须)返回宏:MOSQ_AUTH_PLUGIN_VERSION,这个宏也在头文件mosquitto_plugin.h中进行了定义,它是一个整形数值,在1.4.11版本的mosquitto中该宏的定义形式为:

#define MOSQ_AUTH_PLUGIN_VERSION 2

(3)      接口函数mosquitto_auth_plugin_init

插件初始化函数,在实现此此接口函数时可以初始化用户自己开发的插件,需要注意其第一个参数void **user_data,如果需要在各插件接口之间共享数据的时候,就需要在插件中申请堆内存,然后把要共享的数据放在此内存中,并借助此参数传递所申请的堆内存地址传递给mosquitto,mosquitto后续调用各插件接口函数时,会传递该地址给各插件的接口函数。

(4)      接口函数mosquitto_auth_plugin_cleanup

插件的清理函数,在被封装到了函数mosquitto_security_module_cleanup中,在mosquitto退出的时候会调用这个函数(main函数中)。如果在开发插件的时候申请了资源,就需要在实现这个接口函数时进行释放,例如,创建的连接等。【注意】这个接口函数不要与mosquitto_auth_security_cleanup混淆,而且mosquitto_auth_security_cleanup一定要在这个接口函数之前调用!

(5)      接口函数mosquitto_auth_security_init

插件关于安全相关的初始化,该接口函数被封装在函数mosquitto_security_init中进行调用;【注意】该函数的第三个参数reload,如果传入的值为false则表明是第一次调用,如果是true,则表明它是mosquitto收到了重新加载配置的信号之后,正在进行重新加载配置;

(6)      mosquitto_auth_security_cleanup

插件安全相关的资源清理函数,被封装在函数mosquitto_security_cleanup中进行了调用,在mosquitto退出运行的时候调用;另外,在重新加载配置文件的时候也会调用此函数,然后再调用mosquitto_auth_security_init;

(7)      mosquitto_auth_acl_check

该函数主要对用户(即连接)访问某个topic进行控制,可以对某个topic的读权限(对应mqtt协议的sub操作)进行控制,也可以对写权限控制控制(写权限对应mqtt协议的sub操作),它被封装到了函数mosquitto_acl_check中,在有订阅/发布操作时将被执行用以检查当前操作用户(连接)的权限。调用此函数时需提供连接id,用户名等但是,这里要注意参数access表示校验的权限类型,宏定义如下:

#defineMOSQ_ACL_NONE 0x00

#defineMOSQ_ACL_READ 0x01

#defineMOSQ_ACL_WRITE 0x02

返回宏MOSQ_ERR_SUCCESS表示访问被授权,可以访问该topic,返回MOSQ_ERR_ACL_DENIED表示当前连接不能访问当前topic的读或者写权限。

(8)      mosquitto_auth_unpwd_check

用户名密码校验,它被封装到了函数mosquitto_unpwd_check中;在mqtt的CONNECT控制报文(对应的类型为:0x10)对应的处理函数mqtt3_handle_connect中进行调用(说明:mqtt协议中,在客户端和mosquitto进行tcp三次握手之后,发送给mosquitto的第一条mqtt协议的消息必须为0x10类型的CONNECT控制报文);mosquitto调用函数mosquitto_unpwd_check对连接进行用户名/密码校验的前提是:在CONNECT类型的MQTT控制报文中如果设置了UserName Flag标志位(在CONNECT控制报文的变长头部)以及用户名和密码。

这里你可以将每个用户以及对应的密码保存到redis或者数据库中,然后通过mosquitto的鉴权插件对连接进来的用户名和密码进行校验。但是这种方法在不使用tls时,几乎起不到对连接安全的控制作用,具体原因可以查看下面这篇分析文章:http://blog.csdn.net/houjixin/article/details/53324379

(9)      mosquitto_auth_psk_key_get

当客户端和mosquitto之间采用tls/psk进行通信时,将采用此接口函数获取指定客户端的共享密钥,该接口函数被封装到了函数mosquitto_psk_key_get中。

mosquitto鉴权插件的开发与说明(一)相关推荐

  1. API 鉴权插件上线!支持用户自定义鉴权插件

    0.4.0 版本更新主要围绕这几个方面: 分组独立的 UI,支持分组 API 鉴权 API 测试支持继承 API 鉴权 支持用户自定义鉴权插件,仅需部分配置即可发布鉴权插件 开始介绍功能之前,我想先和 ...

  2. Jenkins Role-based用户鉴权插件

    1.安装 Role-based Authorization Strategy插件 系统管理 > 插件管理 如果没有安装在 Available plugins 搜索安装,下面我是已安装好的~ 2. ...

  3. Nacos 2.1.0 版本发布,支持鉴权及加解密插件

    01 2.1.0 新特性 Aliware 在社区小伙伴的共同努力下,经过了近 1 个月的 Beta 测试后,Nacos 2.1.0 正式发布,支持鉴权及加解密插件,关闭默认支持服务端从 1.X 版本升 ...

  4. iphone鉴权,idps,iPhone蓝牙回控,iphone键盘,iphone手机互联,USB外设开发

    最近一段时间研究iPhone手机互联,这本不是我的专长,但没有办法,项目需要,必须的做啊. 还好现在终于有了点成果,当然这个过程中,也让我学到了很多关于iPhone外设开发的一些东西. 我做的这个项目 ...

  5. 前端开发:关于鉴权的使用总结

    前言 前端开发过程中,关于鉴权(权限的控制)是非常重要的内容,尤其是前端和后端之间数据传递时候的请求鉴权校验.前端鉴权的本质就是控制前端视图层的显示和前端向后台所发送的请求,但是只有前端鉴权,没有后端 ...

  6. 技术探究|Apache Pulsar 认证与鉴权实践指南

    文章摘要 本文整理自 2022 年 8 月 Apache Pulsar Meetup 上傅腾题为<Apache Pulsar 企业级安全实践>的分享.数据安全已经成为企业的一项重要竞争优势 ...

  7. ak sk认证java demo_AK-SK鉴权

    插件名称类别 名称 描述 属性 服务插件 AK/SK 鉴权 gw-ak_sk_auth 用户鉴权 功能描述 配置自己的AK/SK,或是使用网关自动生成的AK/SK,完成认证. 客户端涉及的AK/SK签 ...

  8. Kong 优雅实现微服务网关鉴权,登录场景落地实战篇

    目录 登录实现 B 端登录之后,浏览器存 cookie 登录代码实现细节,cookie设计 网关介绍 API 网关是什么 为什么需要网关 从技术角度来看,什么是Kong? 为什么使用 Kong Kon ...

  9. k8s-身份认证与权限 认证鉴权准入控制- 各种方式带例子-推荐-2023

    # 认证 鉴权 准入控制 ACL 了解 原文:k8s认证.授权与准入控制 - 哪都通临时工 - 博客园 (cnblogs.com) 认证(Authentication):API Server 可以支持 ...

最新文章

  1. atitit. 文件上传带进度条 atiUP 设计 java c# php
  2. Guava之FluentIterable使用示例
  3. pybind传输list
  4. 文档知多少---走出软件作坊:三五个人十来条枪 如何成为开发正规军(二十五)[转]...
  5. 机器学习之神经网络模型-下(Neural Networks: Representation)
  6. Java文本框只有一行数据,Java只允许输入数目字的文本框
  7. python2.7安装pip_RobotFramework安装过程遇到的问题(电脑同时安装python2和3)
  8. LeetCode 1567. 乘积为正数的最长子数组长度
  9. 2020-python小工能
  10. 【Elasticsearch】Elasticsearch 最佳实践系列之分片恢复并发故障
  11. JSLite 的目标:缩小体积,做到 jQuery-free
  12. 移动端浏览器监听返回键
  13. 分布式消息系列:详解RocketMQ的简介与演进、架构设计、关键特性与应用场景
  14. day39-Spring 14-Spring的JDBC模板:DBCP连接池配置
  15. access建立er图_Visio绘制ER图教程
  16. 免费的CRM真的免费吗
  17. 桌面虚拟化 | 同VDI扭打,IDV要如何补齐短板?
  18. 每个男人心中都有一段《西西里的美丽传说》
  19. 分布式事务解决方案:2PC,TCC以及基于消息的最终一致性
  20. 建立一个复数类Complex,其私有数据成员mX和mY表示复数的实部和虚部,构造函数Complex用于对复数的实部和虚部初始化

热门文章

  1. python字典循环添加元素_牛鹭学院:学员笔记|python字典、列表、循环
  2. Spark 性能相关参数配置详解-任务调度篇
  3. IDEA打jar包时出现manifest.mf already exists in vfs解决办法
  4. JAVA :RESTLET开发实例(一)基于JAX-RS的REST服务
  5. Git版本控制常见操作
  6. 华为p20Android怎么解开,华为P20如何获得root权限来解决自启动手机应用程序的问题...
  7. sqlserver 如何将exec的结果保存到一个变量_SQL Server之SQL Trace选项
  8. 顶岗实习周记java方向_会计学院顺利召开2021届毕业生顶岗实习动员大会
  9. Java里氏转换_详解Java设计模式编程中的里氏替换原则
  10. 正则 指定开头结尾_Python核心知识系列:正则表达式与JSON