客户端属性
客户端属性是 EMQX 提供的一种机制,允许开发人员根据不同的应用场景需求,为 MQTT 客户端设置额外的属性。这些属性可以用于 EMQX 的认证授权、数据集成和 MQTT 扩展功能等功能中,在实现灵活开发中发挥重要作用。通过利用客户端元数据,此功能还支持 MQTT 客户端识别的灵活模板化,这对于个性化客户端配置和简化的认证过程至关重要,增强了开发工作的适应性和效率。
工作流程
客户端属性的设置、存储和使用流程如下:
1. 设置客户端属性:
当客户端成功连接到 EMQX 时,EMQX 会触发连接与认证事件,并在此过程中根据预先定义的配置来设置客户端属性。
2. 客户端属性存储与销毁:
一旦设定了属性,它们会以键值对的形式存储在客户端会话的 client_attrs
字段中。当客户端会话结束时,这些属性会被删除。
对于保留会话,客户端接管时,会替换覆盖会话中已有的客户端属性。除此之外,无法通过其他方式修改或删除客户端属性。
3. 使用客户端属性:
EMQX 的其他功能允许在相关配置项中使用 ${client_attrs.NAME}
占位符,动态提取属性值,并将其配置或数据的一部分来使用。
设置客户端属性
当客户端成功连接到 EMQX 时,EMQX 会触发连接和认证事件,并根据预先定义的配置来设置客户端属性。目前支持以下两种设置方式:
- 从客户端元数据中提取
- 在客户端认证过程中设置
从客户端元数据中提取
通过预设的配置,从客户端连接时的用户名、客户端 ID 等元数据中提取并处理生成子串,并将其设置为客户端属性。这个过程是在认证之前发生的,确保了属性可以在认证过程中使用。您可以通过配置文件或 Dashboard 配置客户端属性功能。
想要在 Dashboard 中配置客户端属性,点击左侧导航中的管理 -> MQTT 配置,在客户端属性配置中点击添加,填写属性名称和属性表达式。
其中,属性为属性的名称,属性表达式为属性的提取配置。
属性表达式支持使用 Variform 表达式以及其中的预定义的函数对值进行动态处理。例如:
- 要提取由点分隔的客户端 ID 的前缀:
nth(1, tokens(clientid, '.'))
- 要截取用户名部分信息:
substr(username, 0, 5)
对应的配置文件如下:
mqtt {
client_attrs_init = [
{
expression = "nth(1, tokens(clientid, '.'))"
set_as_attr = clientid_prefix
},
{
expression = "substr(username, 0, 5)"
set_as_attr = sub_username
}
]
}
在属性表达式中支持配置以下值:
clientid
:客户端 IDusername
:用户名cn
:TLS 证书的 CN 字段dn
:TLS 证书的 DN 字段user_property.*
:从 MQTT CONNECT 数据包的 User-Property 中提取属性值,例如user_property.foo
zone
:继承自 MQTT 监听器的 zone 名称
更多客户端属性配置的详细信息,请参见 EMQX 开源版配置手册和 EMQX 企业版配置手册。
在客户端认证过程中设置
在客户端认证过程中,可以通过认证器返回的信息设置客户端属性,目前支持:
- JWT 认证:签发 Token 时,在 Payload 中通过
client_attrs
字段设置客户端属性。 - HTTP 认证:在认证成功的 HTTP 响应中,通过
client_attrs
字段设置客户端属性。
属性的键值均要求是字符串。这种方式允许根据认证结果动态地设置属性,增加了使用的灵活性。
合并认证数据
当同时通过 2 种设置方式,或使用多个认证器设置客户端属性时,EMQX 会按照属性名称以及设置的先后顺序对属性进行合并:
- 从客户端元数据中提取的属性将被认证器中设置的属性覆盖。
- 认证链中多个认证器设置属性时,后设置的属性将覆盖前面的属性。
客户端属性的应用
在其他 EMQX 功能中,客户端属性可以通过 ${client_attrs.NAME}
占位符提取,并作为配置或数据的一部分来使用。目前仅支持用于客户端认证与授权,更多功能将来后续开放。
客户端认证
通过认证占位符用于 SQL 语句、查询命令或 HTTP 请求体中的动态参数,例如:
# MySQL/PostgreSQL - 认证查询 SQL
SELECT password_hash, salt, is_superuser FROM mqtt_user WHERE sn = ${client_attrs.sn} LIMIT 1
# HTTP - 认证请求 Body
{
"sn": "${client_attrs.sn}",
"password": "${password}"
}
具体使用方式请参考每个认证器对应的文档。
TIP
客户端认证仅能使用从客户端元数据中提取设置的属性。
客户端授权
通过数据查询占位符与主题占位符,用于 SQL 语句、查询命令,以及主题当中。
示例场景:
通过客户端属性,为每个客户端设置了各自的 role
, productId
, deviceId
属性。需要将这些属性用于授权检查中:
- role: 用来限制客户端的访问权限,只允许 role 为
admin
的客户端订阅和发布管理消息,如匹配admin/#
的主题。 - productId: 用来限制客户端只能订阅适用于当前产品的 OTA 消息,如
OTA/{prodoctId}
。 - deviceId: 用来限制客户端只能在属于自己的主题上发布和订阅消息:
- 发布:
up/{productId}/{deviceId}
- 订阅:
down/{productId}/{deviceId}
- 发布:
可以使用授权-内置数据库配置如下规则实现:
权限 | 操作 | 主题 |
---|---|---|
允许 | 订阅和发布 | ${client_attrs.role}/# |
允许 | 订阅 | OTA/${client_attrs.productId} |
允许 | 发布 | up/${client_attrs.productId}/${client_attrs.deviceId} |
允许 | 订阅 | down/${client_attrs.productId}/${client_attrs.deviceId} |
相较于直接使用客户端 ID 等静态属性,更灵活地管理客户端的授权。这种灵活性使得我们能够基于不同的角色、产品和设备来细粒度地控制客户端的访问权限。