概述
身份认证是大多数应用的重要组成部分,MQTT 协议支持用户名密码认证,启用身份认证能有效阻止非法客户端的连接。
本文介绍了 EMQ X 支持的认证方式以及对应插件的配置方法。
EMQ X MQTT Broker 简介
EMQ X Broker 是基于高并发的 Erlang/OTP 语言平台开发,支持百万级连接和分布式集群架构,发布订阅模式的开源 MQTT 消息服务器。
EMQ X Broker 在全球物联网市场广泛应用。无论是产品原型设计、物联网创业公司、还是大规模的商业部署,EMQ X Broker 都支持开源免费使用。
认证方式
EMQ X 支持使用内置数据源(文件、内置数据库)、JWT、外部主流数据库和自定义 HTTP API 作为身份认证数据源。
连接数据源、进行认证逻辑通过插件实现的,每个插件对应一种认证方式,使用前需要启用相应的插件。
客户端连接时插件通过检查其 username/clientid 和 password 是否与指定数据源的信息一致来实现对客户端的身份认证。
EMQ X 支持的认证方式:
内置数据源
- 用户名认证
- Cliend ID 认证
使用配置文件与 EMQ X 内置数据库提供认证数据源,通过 REST API 进行管理,足够简单轻量。
外部数据库
- MySQL 认证
- PostgreSQL 认证
- Redis 认证
- MongoDB 认证
外部数据库可以存储大量数据,同时方便与外部设备管理系统集成。
其他
HTTP 认证
JWT 认证
JWT 认证可以批量签发认证信息,HTTP 认证能够实现复杂的认证鉴权逻辑。
更改插件配置后需要重启插件才能生效,部分认证鉴权插件包含 ACL 功能。
认证结果
任何一种认证方式最终都会返回一个结果:
- 认证成功:经过比对客户端认证成功
- 认证失败:经过比对客户端认证失败,数据源中密码与当前密码不一致
- 忽略认证(ignore):当前认证方式中未查找到认证数据,无法显式判断结果是成功还是失败,交由认证链下一认证方式或匿名认证来判断
匿名认证
EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQ X。没有启用认证插件或认证插件没有显式允许/拒绝(ignore)连接请求时,EMQ X 将根据匿名认证启用情况决定是否允许客户端连接。
配置匿名认证开关:
# etc/emqx.conf |
生产环境中请禁用匿名认证。
密码加盐规则与哈希方法
EMQ X 多数认证插件中可以启用哈希方法,数据源中仅保存密码密文,保证数据安全。
启用哈希方法时,用户可以为每个客户端都指定一个 salt(盐)并配置加盐规则,数据库中存储的密码是按照加盐规则与哈希方法处理后的密文。
以 MySQL 认证为例:
加盐规则与哈希方法配置:
# etc/plugins/emqx_auth_mysql.conf |
如何生成认证信息
- 为每个客户端分用户名、Client ID、密码以及 salt(盐)等信息
- 使用与 MySQL 认证相同加盐规则与哈希方法处理客户端信息得到密文
- 将客户端信息写入数据库,客户端的密码应当为密文信息
EMQ X 身份认证流程
- 根据配置的认证 SQL 结合客户端传入的信息,查询出密码(密文)和 salt(盐)等认证数据,没有查询结果时,认证将终止并返回 ignore 结果
- 根据配置的加盐规则与哈希方法计算得到密文,没有启用哈希方法则跳过此步
- 将数据库中存储的密文与当前客户端计算的到的密文进行比对,比对成功则认证通过,否则认证失败
写入数据的加盐规则、哈希方法与对应插件的配置一致时认证才能正常进行。更改哈希方法会造成现有认证数据失效。
认证链
当同时启用多个认证方式时,EMQ X 将按照插件开启先后顺序进行链式认证:
- 一旦认证成功,终止认证链并允许客户端接入
- 一旦认证失败,终止认证链并禁止客户端接入
- 直到最后一个认证方式仍未通过,根据匿名认证配置判定
- 匿名认证开启时,允许客户端接入
- 匿名认证关闭时,禁止客户端接入
同时只启用一个认证插件可以提高客户端身份认证效率。
MySQL 认证
MySQL 认证使用外部 MySQL 数据库作为认证数据源,可以存储大量数据,同时方便与外部设备管理系统集成。
插件:
emqx_auth_mysql |
emqx_auth_mysql 插件同时包含 ACL 功能,可通过注释禁用。
要启用 MySQL 认证,需要在 etc/plugins/emqx_auth_mysql.conf
中配置以下内容:
MySQL 连接信息
MySQL 基础连接信息,需要保证集群内所有节点均能访问。
# etc/plugins/emqx_auth_mysql.conf |
默认表结构
MySQL 认证默认配置下需要确保数据库中有下表:
CREATE TABLE `mqtt_user` ( |
默认配置下示例数据如下:
INSERT INTO `mqtt_user` ( `username`, `password`, `salt`) |
启用 MySQL 认证后,你可以通过用户名: emqx,密码:public 连接。
这是默认配置使用的表结构,熟悉该插件的使用后你可以使用任何满足条件的数据表进行认证。
加盐规则与哈希方法
MySQL 认证支持配置加盐规则与哈希方法。
# etc/plugins/emqx_auth_mysql.conf |
认证 SQL(auth_query)
进行身份认证时,EMQ X 将使用当前客户端信息填充并执行用户配置的认证 SQL,查询出该客户端在数据库中的认证数据。
# etc/plugins/emqx_auth_mysql.conf |
你可以在认证 SQL 中使用以下占位符,执行时 EMQ X 将自动填充为客户端信息:
- %u:用户名
- %c:Client ID
- %C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效
- %d:TLS 证书 subject,仅当 TLS 连接时有效
你可以根据业务需要调整认证 SQL,如添加多个查询条件、使用数据库预处理函数,以实现更多业务相关的功能。但是任何情况下认证 SQL 需要满足以下条件:
- 查询结果中必须包含 password 字段,EMQ X 使用该字段与客户端密码比对
- 如果启用了加盐配置,查询结果中必须包含 salt 字段,EMQ X 使用该字段作为 salt(盐)值
- 查询结果只能有一条,多条结果时只取第一条作为有效数据
可以在 SQL 中使用 AS 语法为字段重命名指定 password,或者将 salt 值设为固定值。
特殊说明
MySQL 8.0 及以后版本使用了 caching_sha2_password
作为默认身份验证插件,受限于客户端驱动你必须将其更改为 mysql_native_password
插件:
ALTER USER 'your_username'@'your_host' IDENTIFIED WITH mysql_native_password BY 'your_password'; |
Redis 认证
Redis 认证使用外部 Redis 数据库作为认证数据源,可以存储大量数据,同时方便与外部设备管理系统集成。
插件:
emqx_auth_redis |
emqx_auth_redis 插件同时包含 ACL 功能,可通过注释禁用。
要启用 Redis 认证,需要在 etc/plugins/emqx_auth_redis.conf
中配置以下内容:
Redis 连接信息
Redis 基础连接信息,需要保证集群内所有节点均能访问。
# etc/plugins/emqx_auth_redis.conf |
默认数据结构
Redis 认证默认配置下使用哈希表存储认证数据,使用 mqtt_user:
作为 Redis 键前缀,数据结构如下:
redis> hgetall mqtt_user:emqx |
默认配置下示例数据如下:
HMSET mqtt_user:emqx password public salt wivwiv |
启用 Redis 认证后,你可以通过用户名: emqx,密码:public 连接。
这是默认配置使用的数据结构,熟悉该插件的使用后你可以使用任何满足条件的数据结构进行认证。
加盐规则与哈希方法
Redis 认证支持配置 加盐规则与哈希方法,默认存储明文密码不做处理:
# etc/plugins/emqx_auth_redis.conf |
认证查询命令(auth query cmd)
进行身份认证时,EMQ X 将使用当前客户端信息填充并执行用户配置的认证查询命令,查询出该客户端在 Redis 中的认证数据。
# etc/plugins/emqx_auth_redis.conf |
你可以在命令中使用以下占位符,执行时 EMQ X 将自动填充为客户端信息:
- %u:用户名
- %c:Client ID
- %C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效
- %d:TLS 证书 subject,仅当 TLS 连接时有效
你可以根据业务需要调整认证查询命令,使用任意 Redis 支持的命令,但是任何情况下认证查询命令需要满足以下条件:
- 查询结果中第一个数据必须为 password,EMQ X 使用该字段与客户端密码比对
- 如果启用了加盐配置,查询结果中第二个数据必须是 salt 字段,EMQ X 使用该字段作为 salt(盐)值
MongoDB 认证
MongoDB 认证使用外部 MongoDB 数据库作为认证数据源,可以存储大量数据,同时方便与外部设备管理系统集成。
插件:
emqx_auth_mongo |
emqx_auth_mongo 插件同时包含 ACL 功能,可通过注释禁用。
要启用 MongoDB 认证,需要在 etc/plugins/emqx_auth_mongo.conf
中配置以下内容:
MongoDB 连接信息
MongoDB 基础连接信息,需要保证集群内所有节点均能访问。
# etc/plugins/emqx_auth_mongo.conf |
默认表结构
MongoDB 认证默认配置下需要确保数据库中有如下集合:
{ |
默认配置下示例数据如下:
use mqtt |
启用 MongoDB 认证后,你可以通过用户名: emqx,密码:public 连接。
这是默认配置使用的集合结构,熟悉该插件的使用后你可以使用任何满足条件的集合进行认证。
加盐规则与哈希方法
MongoDB 认证支持配置加盐规则与哈希方法:
# etc/plugins/emqx_auth_mongo.conf |
认证查询(auth_selector)
进行身份认证时,EMQ X 将使用当前客户端信息填充并执行用户配置的认证 SQL,查询出该客户端在数据库中的认证数据。
MongoDB 支持配置集合名称、密码字段、selector 命令
# etc/plugins/emqx_auth_mongo.conf |
你可以在认证查询(selector)中使用以下占位符,执行时 EMQ X 将自动填充为客户端信息:
- %u:用户名
- %c:Client ID
- %C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效
- %d:TLS 证书 subject,仅当 TLS 连接时有效
你可以根据业务需要调整认证查询,如添加多个查询条件、使用数据库预处理函数,以实现更多业务相关的功能。但是任何情况下认证查询需要满足以下条件:
- 查询结果中必须包含 password 字段,EMQ X 使用该字段与客户端密码比对
- 如果启用了加盐配置,查询结果中必须包含 salt 字段,EMQ X 使用该字段作为 salt(盐)值
- MongoDB 使用 findOne 查询命令,确保你期望的查询结果能够出现在第一条数据中
JWT 认证
JWT 认证基于 Token 的鉴权机制,不依赖服务端保留客户端的认证信息或者会话信息,在持有密钥的情况下可以批量签发认证信息,是最简便的认证方式。
插件:
emqx_auth_jwt |
认证原理
客户端使用 Token 作为用户名或密码(取决于插件配置),发起连接时 EMQ X 使用配置中的密钥、证书进行解密,如果能成功解密则认证成功,否则认证失败。
默认配置下启用 JWT 认证后,你可以通过任意用户名,以下密码进行连接:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImF1dGhvciI6IndpdndpdiIsInNpdGUiOiJodHRwczovL3dpdndpdi5jb20ifSwiZXhwIjoxNTgyMjU1MzYwNjQyMDAwMCwiaWF0IjoxNTgyMjU1MzYwfQ.FdyAx2fYahm6h3g47m88ttyINzptzKy_speimyUcma4 |
配置项
要启用 JWT 认证,需要在 etc/plugins/emqx_auth_jwt.conf
中配置以下内容:
# etc/plugins/emqx_auth_jwt.conf |
JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限,使用 JWT 时建议启用 TLS 加密传输。
JWT 使用过程中无法在过期前废止某个 Token,请妥善设置有效时长并保管好密钥等加密信息。
HTTP 认证
HTTP 认证使用外部自建 HTTP 应用认证数据源,根据 HTTP API 返回的数据判定认证结果,能够实现复杂的认证鉴权逻辑。
插件:
emqx_auth_http |
emqx_auth_http 插件同时包含 ACL 功能,可通过注释禁用。
认证原理
EMQ X 在设备连接事件中使用当前客户端相关信息作为参数,向用户自定义的认证服务发起请求查询权限,通过返回的 HTTP 响应状态码 (HTTP statusCode) 来处理认证请求。
- 认证失败:API 返回 4xx 状态码
- 认证成功:API 返回 200 状态码
- 忽略认证:API 返回 200 状态码且消息体 ignore
HTTP 请求信息
HTTP API 基础请求信息,配置证书、请求头与重试规则。
# etc/plugins/emqx_auth_http.conf |
加盐规则与哈希方法
HTTP 在请求中传递明文密码,加盐规则与哈希方法取决于 HTTP 应用。
认证请求
进行身份认证时,EMQ X 将使用当前客户端信息填充并执行用户配置的认证 SQL,查询出该客户端在数据库中的认证数据。
# etc/plugins/emqx_auth_http.conf |
HTTP 请求方法为 GET 时,请求参数将以 URL 查询字符串的形式传递;POST、PUT 请求则将请求参数以普通表单形式提交(content-type 为 x-www-form-urlencoded)。
你可以在认证请求中使用以下占位符,请求时 EMQ X 将自动填充为客户端信息:
- %u:用户名
- %c:Client ID
- %a:客户端 IP 地址
- %r:客户端接入协议
- %P:明文密码
- %p:客户端端口
- %C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效
- %d:TLS 证书 subject,仅当 TLS 连接时有效
推荐使用 POST 与 PUT 方法,使用 GET 方法时明文密码可能会随 URL 被记录到传输过程中的服务器日志中。