简介
身份认证是大多数应用的重要组成部分,MQTT 协议支持用户名密码认证,启用身份认证能有效阻止非法客户端的连接。
EMQ X 中的认证指的是当一个客户端连接到 EMQ X 的时候,通过服务器端的配置来控制客户端连接服务器的权限。
EMQ X 的认证支持包括两个层面:
- MQTT 协议本身在 CONNECT 报文中指定用户名和密码,EMQ X 以插件形式支持基于 Username、ClientID、HTTP、JWT、LDAP 及各类数据库如 MongoDB、MySQL、PostgreSQL、Redis 等多种形式的认证。
- 在传输层上,TLS 可以保证使用客户端证书的客户端到服务器的身份验证,并确保服务器向客户端验证服 务器证书。也支持基于 PSK 的 TLS/DTLS 认证。
认证方式
EMQ X 支持的认证方式:
内置数据源:
- Username 认证
- Cliend ID 认证
外部数据库:
- LDAP 认证
- MySQL 认证
- PostgreSQL 认证
- Redis 认证
- MongoDB 认证
其他:
- HTTP 认证
- JWT 认证
认证结果
任何一种认证方式最终都会返回一个结果:
- 认证成功:经过比对客户端认证成功
- 认证失败:经过比对客户端认证失败,数据源中密码与当前密码不一致
- 忽略认证(ignore):当前认证方式中未查找到认证数据,无法显式判断结果是成功还是失败,交由认证链下一认证方式或匿名认证来判断
匿名认证
EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQ X。
没有启用认证插件或认证插件没有显式允 许/拒绝(ignore)连接请求时,EMQ X 将根据匿名认证启用情况决定是否允许客户端连接。
配置匿名认证开关:
#关闭匿名认证
vi /etc/emqx/emqx.conf
## Allow anonymous authentication by default if no auth plugins loaded.
## Notice: Disable the option in production deployment!
##
## Value: true | false
#改为false
allow_anonymous = false
#重启emqx
emqx restart
加密加盐及认证流程
加密加盐
EMQ X 多数认证插件中可以启用哈希方法,数据源中仅保存密码密文,保证数据安全。
启用哈希方法时,用户可以为每个客户端都指定一个 salt(盐)并配置加盐规则,数据库中存储的密码是按照加盐规则与哈希方法处理后的密文。
以 MySQL 认证为例,加盐规则与哈希方法配置:
# etc/plugins/emqx_auth_mysql.conf
## 不加盐,仅做哈希处理
auth.mysql.password_hash = sha256
## salt 前缀:使用 sha256 加密 salt + 密码 拼接的字符串
auth.mysql.password_hash = salt,sha256
## salt 后缀:使用 sha256 加密 密码 + salt 拼接的字符串
auth.mysql.password_hash = sha256,salt
## pbkdf2 with macfun iterations dklen
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
## auth.mysql.password_hash = pbkdf2,sha256,1000,20
如何生成认证信息:
- 为每个客户端分用户名、Client ID、密码以及 salt(盐)等信息
- 使用与 MySQL 认证相同加盐规则与哈希方法处理客户端信息得到密文
- 将客户端信息写入数据库,客户端的密码应当为密文信息
EMQ X 身份认证流程
- 根据配置的认证 SQL 结合客户端传入的信息,查询出密码(密文)和 salt(盐)等认证数据,没有查询结果时,认证将终止并返回 ignore 结果
- 根据配置的加盐规则与哈希方法计算得到密文,没有启用哈希方法则跳过此步
- 将数据库中存储的密文与当前客户端计算的到的密文进行比对,比对成功则认证通过,否则认证失败
PostgreSQL 认证功能逻辑图:
认证链:
当同时启用多个认证方式时,EMQ X 将按照插件开启先后顺序进行链式认证:
- 一旦认证成功,终止认证链并允许客户端接入
- 一旦认证失败,终止认证链并禁止客户端接入
- 直到最后一个认证方式仍未通过,根据匿名认证配置判定
-
- 匿名认证开启时,允许客户端接入
- 匿名认证关闭时,禁止客户端接入
Mnesia认证
Mnesia 认证使用 EMQ X 内置 Mnesia 数据库存储客户端 Client ID/Username 与密码,支持通过 HTTP API 管理认证数据。
Mnesia 认证不依赖外部数据源,使用上足够简单轻量。
点击启动,让该插件运行
Mnesia 认证默认使用 sha256 进行密码哈希加密,可在 etc/plugins/emqx_auth_mnesia.conf
中更改:
配置哈希方法后,新增的预设认证数据与通过 HTTP API 添加的认证数据将以哈希密文存储在 EMQ X 内置数据库中。
预设认证数据
可以通过配置文件预设认证数据,编辑配置文件:etc/plugins/emqx_auth_mnesia.conf
预设认证数据格式兼容 emqx_auth_clientid
与 emqx_auth_username
插件的配置格式
## Password hash.
##
## Value: plain | md5 | sha | sha256 | sha512
auth.mnesia.password_hash = sha256
##--------------------------------------------------------------------
## ClientId Authentication
##--------------------------------------------------------------------
## Examples
auth.client.1.clientid = id
auth.client.1.password = passwd
##auth.client.2.clientid = dev:devid
##auth.client.2.password = passwd2
##auth.client.3.clientid = app:appid
##auth.client.3.password = passwd3
##auth.client.4.clientid = client~!@#$%^&*()_+
##auth.client.4.password = passwd~!@#$%^&*()_+
##--------------------------------------------------------------------
## Username Authentication
##--------------------------------------------------------------------
## Examples:
auth.user.1.username = admin
auth.user.1.password = public
##auth.user.2.username = feng@emqtt.io
##auth.user.2.password = public
##auth.user.3.username = name~!@#$%^&*()_+
##auth.user.3.password = pwsswd~!@#$%^&*()_+
插件启动时将读取预设认证数据并加载到 EMQ X 内置数据库中,节点上的认证数据会在此阶段同步至集群中。
username测试
测试username,password:
账号密码为 admin/public
增加用户
这里增删改查都是完全遵守restful格式,后续不再演示
查看指定用户user的信息:
websocket连接
mqttx客户端工具验证
下载地址:https://github.com/emqx/MQTTX/releases/download/v1.6.0/MQTTX.Setup.1.6.0.exe
连接成功:
添加一个订阅者:
client_id认证
在前面,我添加了一个client_id信息,账号id为id
,密码为passwd
使用mqttx来验证client_id认证方式:
这里注意client_id和password填入自己配置的新,这里尽管username没有,但是必须填入一个任意的值,为空则会连接失败
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 应用
基本使用
修改配置文件
开启该插件:
编辑http认证配置文件
vi /etc/emqx/plugins/emqx_auth_http.conf
#其余配置尚未修改,这里就不展示了
auth.http.auth_req.url = http://192.168.40.128:8991/mqtt/auth
auth.http.auth_req.method = post
auth.http.auth_req.headers.content_type = application/x-www-form-urlencoded
auth.http.auth_req.params = clientid=%c,username=%u,password=%P
你可以在认证请求中使用以下占位符,请求时 EMQ X 将自动填充为客户端信息:
- %u:用户名
- %c:Client ID
- %a:客户端 IP 地址
- %r:客户端接入协议
- %P:明文密码
- %p:客户端端口
- %C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效
- %d:TLS 证书 subject,仅当 TLS 连接时有效
开发认证服务
这里基于springboot快速开发一套认证服务
application.yml
server:
port: 8991
spring:
application:
name: emq-demo
AuthController.java
@RestController
@RequestMapping("/mqtt")
public class AuthController {
Logger logger = LoggerFactory.getLogger(AuthController.class);
Map<String, String> userMap = new HashMap<>();
@PostConstruct
public void init(){
userMap.put("user1","user1");
userMap.put("user2","user2");
}
@PostMapping("/auth")
public ResponseEntity<HttpStatus> auth(String username, String clientid, String password){
logger.info("开始进入auth方法");
logger.info("username:{}; clientid:{}; password:{}", username, clientid, password);
String passwd = userMap.get(username);
if(!StringUtils.hasLength(passwd)){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return !password.equals(passwd) ? new ResponseEntity<>(HttpStatus.UNAUTHORIZED) : new ResponseEntity<>(HttpStatus.OK);
}
}
打包后,传到服务器并启动
测试http认证
连接成功:
springboot认证服务日志:
emqx认证就介绍到这里,关于其他的认证方式可以参考官方文档:https://docs.emqx.cn/broker/v4.3/advanced/auth.html#认证方式