# 使用 Redis 进行密码认证

作为密码认证方式的一种，EMQX 支持通过集成 Redis 进行密码认证。EMQX 支持三种 Redis 部署模式：单节点、[Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/)、[Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/)，本节将介绍如何进行相关配置。

::: tip 前置准备

熟悉 [EMQX 认证基本概念](../authn/authn.md)
:::

## 数据结构与查询指令

Redis 认证器支持使用 [Redis hashes](https://redis.io/docs/latest/develop/data-types/hashes/) 存储认证数据，用户需要提供一个查询指令模板，且确保查询结果包含以下字段：

- `password_hash`: 必需，数据库中的明文或散列密码字段
- `salt`: 可选，为空或不存在时视为空盐（`salt = ""`）
- `is_superuser`: 可选，标记当前客户端是否为超级用户，默认为 `false`

例如，您希望添加一位名用户名为 `emqx_u`、密码为 `public`、盐值为 `salt_foo123`、散列方式为 `sha256` 且超级用户标志为 `true` 的用户：

```bash
>redis-cli
127.0.0.1:6379> HSET mqtt_user:emqx_u is_superuser 1 salt slat_foo123 password_hash 44edc2d57cde8d79c98145003e105b90a14f1460b79186ea9cfe83942fc5abb5
(integer) 0
```

对应的配置参数为：

- 密码加密方式：`sha256`
- 加盐方式：`suffix`
- 命令：`HMGET mqtt_user:${username} password_hash salt is_superuser`

::: tip
`password_hash` 这一字段名称直观表明用户应当在数据库中存储散列密码。但鉴于 Redis 没有类似 MySQL 的 `as` 语法，我们保留了 4.x 对 `password` 的兼容。

所以，您也可以将 `cmd` 配置为 `HMGET mqtt_user:${username} password salt is_superuser`。
:::

## 通过 Dashboard 配置

1. 在 EMQX Dashboard 页面上点击左侧导航栏的**访问控制** -> **客户端认证**
2. 在**客户端认证**页面，点击**+ 创建**。
3. 依次选择**认证方式**为 `Password-Based`，**数据源**为 `Redis`，点击**下一步**进入**配置参数**页签：

<img src="./assets/authn-redis.png" alt="Authentication with redis" style="zoom:67%;" />

4. 按照以下说明配置数据源：

   - Redis 数据库的连接设置：

- **部署模式**：选择 Redis 数据库的部署模式，可选值：**单节点**、**Sentinel**、**Cluster**。

- **服务器地址**：填入 Redis 服务器地址 (`host:port`) ；当部署模式选为 Sentinel 或 Cluster，您需在此提供所有相关 Redis 服务器的地址，不同地址之间以 `,` 分隔，格式为 `host1:port1,host2:port2,...`。

- **Sentinel 名字**（仅需在**部署模式**设置为 **Sentinel** 时设置）：指定 Redis Sentinel 配置需要的[主服务器名称](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/)。

- **数据库**：整数，用于指定 Redis 数据库的 Index。

- **用户名**：指定用于连接 Redis 的用户名。如果您的 Redis 实例启用了 [Redis ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/#create-and-edit-user-acls-with-the-acl-setuser-command)（在 Redis 6.0 引入）进行身份验证，则此字段为必填项。如果您的 Redis 使用默认用户（未启用或未强制使用 ACL），则可以留空此字段。

  ::: tip 提示

  `username` 字段从 EMQX 5.2.0 版本开始支持。请确保您的部署版本为 5.2.0 或更高，以使用 Redis ACL 功能。

  :::

- **密码**：指定用于连接 Redis 的用户密码。若 Redis 实例启用了身份验证，该字段为必填项。

  - 如果填写了用户名，则此密码必须与 Redis ACL 配置中的凭据匹配。
  - 如果未填写用户名，则此密码将用于以 Redis 的 `default` 用户身份进行身份验证（前提是默认用户已启用）。

   - 认证加密算法相关的配置：

     - **密码加密方式**：选择应用于明文密码的哈希算法，在将结果存储到数据库之前对密码进行加密。可选算法包括 `plain`、`md5`、`sha`、`sha256`、`sha512`、`bcrypt` 和 `pbkdf2`。具体配置取决于所选择的算法：
       - 选择 `md5`、`sha`、`sha256` 或 `sha512` 算法，需配置：
         - **加盐方式**：用于指定盐和密码的组合方式，可选值：`suffix`（在密码尾部加盐）、`prefix`（在密码头部加盐）、`disable`（不启用）。如果您不需要将用户凭据从外部存储迁移到 EMQX 内置数据库，可以保持默认值。
         - 生成的哈希值以十六进制字符串表示，并与存储的凭据进行不区分大小写的比对。
       - 选择 `plain`：
         - **加盐方式**：应设置为 `disable`。
       - 选择 `bcrypt` 算法，需配置:
         - **Salt Rounds**：指定散列需要的计算次数（2^Salt Rounds），也称成本因子。默认值：`10`，可选值：`5` 到 `10`；数值越高，加密的安全性越高，因此建议采用较大的值，但相应的用户验证的耗时也会增加，您可根据业务需求进行配置。
       - 选择 `pbkdf2` 算法，需配置：
         - **伪随机函数**：指定生成密钥使用的散列函数，如 `sha256` 等。
         - **迭代次数**：指定散列次数，默认值：`4096`。<!--后续补充取值范围-->
         - **密钥长度**（可选）：指定希望得到的密钥长度。如不指定，密钥长度将由**伪随机函数**确定。
         - 生成的哈希值以十六进制字符串表示，并与存储的凭据进行不区分大小写的比对。

   - **调用条件**：一个 Variform 表达式，用于控制是否将此 Redis 认证器应用于客户端连接。该表达式会根据客户端的属性（例如 `username`、`clientid`、`listener` 等）进行评估。如果表达式的结果为字符串 `"true"`，则会触发认证器。否则，认证器将被跳过。有关调用条件的更多信息，请参见[认证器调用条件](./authn.md#认证器调用条件)。

   - **启用 TLS**：如果要启用TLS，请打开切换按钮。有关启用 TLS 的更多信息，请参见[网络和 TLS](../../network/overview.md)。

- **密码加密方式**：选择应用于明文密码的哈希算法，在将结果存储到数据库之前对密码进行加密。可选算法包括 `plain`、`md5`、`sha`、`sha256`、`sha512`、`bcrypt` 和 `pbkdf2`。具体配置取决于所选择的算法：
  - 选择 `md5`、`sha`、`sha256` 或 `sha512` 算法，需配置：
    - **加盐方式**：用于指定盐和密码的组合方式，可选值：`suffix`（在密码尾部加盐）、`prefix`（在密码头部加盐）、`disable`（不启用）。如果您不需要将用户凭据从外部存储迁移到 EMQX 内置数据库，可以保持默认值。
    - 生成的哈希值以十六进制字符串表示，并与存储的凭据进行不区分大小写的比对。
  - 选择 `plain`：
    - **加盐值方式**：应设置为 `disable`。
  - 选择 `bcrypt` 算法，需配置:
    - **Salt Rounds**：指定散列需要的计算次数（2^Salt Rounds），也称成本因子。默认值：`10`，可选值：`5` 到 `10`；数值越高，加密的安全性越高，因此建议采用较大的值，但相应的用户验证的耗时也会增加，您可根据业务需求进行配置。
  - 选择 `pbkdf2` 算法，需配置：
    - **伪随机函数**：指定生成密钥使用的散列函数，如 `sha256` 等。
    - **迭代次数**：指定散列次数，默认值：`4096`。<!--后续补充取值范围-->
    - **密钥长度**（可选）：指定希望得到的密钥长度。如不指定，密钥长度将由**伪随机函数**确定。
    - 生成的哈希值以十六进制字符串表示，并与存储的凭据进行不区分大小写的比对。
- **命令**：Redis 查询命令。

   - **高级设置**：在此部分设置并发连接。
     - **连接池大小**（可选）：填入一个整数用于指定从 EMQX 节点到 Redis 数据库的并发连接数；默认值：`8`。
     

5. 点击**创建**完成相关配置。

## 通过配置文件配置

您可通过配置文件完成相关配置。 <!-- 详细配置步骤请参考 [authn-redis:standalone](../../configuration/configuration-manual.html#authn-redis:standalone)、[authn-redis:sentinel](../../configuration/configuration-manual.html#authn-redis:sentinel) 与 [authn-redis:cluster](../../configuration/configuration-manual.html#authn-redis:cluster)。-->

以下为各部署模式下的配置文件示例：

:::: tabs type:card

::: tab 单节点

```hcl
{
  mechanism = password_based
  backend = redis

  redis_type = single
  server = "127.0.0.1:6379"

  password_hash_algorithm {
      name = sha256
      salt_position = suffix
  }

  cmd = "HMGET mqtt_user:${username} password_hash salt is_superuser"
  database = 1
  password = "public"
  auto_reconnect = true
}
```

:::

::: tab Redis Sentinel 部署模式

```hcl
{
  mechanism = password_based
  backend = redis

  redis_type = sentinel
  servers = "10.123.13.11:6379,10.123.13.12:6379"
  sentinel = "mymaster"

  password_hash_algorithm {
      name = sha256
      salt_position = suffix
  }

  cmd = "HMGET mqtt_user:${username} password_hash salt is_superuser"
  database = 1
  password = "public"
  auto_reconnect = true
}
```

:::

::: tab Redis Cluster 部署模式

```hcl
{
  mechanism = password_based
  backend = redis

  redis_type = cluster
  servers = "10.123.13.11:6379,10.123.13.12:6379"

  password_hash_algorithm {
      name = sha256
      salt_position = suffix
  }

  cmd = "HMGET mqtt_user:${username} password_hash salt is_superuser"
  password = "public"
  auto_reconnect = true
}
```

:::

::::
