# JWT 认证

[JWT](https://jwt.io/) 认证是基于 Token 的鉴权机制，不依赖服务端保留客户端的认证信息或者会话信息，在持有密钥的情况下可以批量签发认证信息，是最简便的认证方式。

::: tip 提示

Serverless 部署不支持 JWT 认证。

:::


## JWT 认证原理

客户端使用用户名或密码字段携带 JWT（取决于模块配置），发起连接时 EMQX Cloud 使用配置中的密钥、证书进行解密，如果能成功解密则认证成功，否则认证失败。

如果签名验证成功，JWT 认证器将继续检查声明。JWT 认证器会根据这些声明如 `iat`（签发时间）、`nbf`（不早于）和 `exp`（过期时间）来主动检查 JWT 的有效性。还可以指定额外的自定义声明进行认证。只有当签名和声明的认证都成功时，客户端才被授权访问。

默认配置下启用 JWT 认证后，你可以通过任意用户名+以下密码进行连接，即通过默认的密钥字段 `emqxsecret` 做验证：

```bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFTVFYIENsb3VkIiwiaWF0IjoxNTE2MjM5MDIyfQ.-k9Ggc6L_Jxq4uUf9xwdJpwRrS3PquL-JZKtAJoOvBo
```

> 上述 JWT Token 仅做测试使用，您可以使用适当的工具生成适合您业务需求的 JWT Token，参考[如何生成 JWT](#如何生成-jwt)。
>

### 如何生成 JWT

本节将逐步介绍如何生成用于 EMQX 客户端认证的有效 JWT。

#### 前提条件

- 一个密钥（HMAC 算法使用）或私钥（RSA/ECDSA 使用）
- 一个生成 JWT 的工具或代码库（例如 [jwt.io](https://jwt.io)、[jjwt](https://www.jsonwebtoken.io/)、[mkjwk](https://mkjwk.org)、Python、Node.js）
- 知道 EMQX 所期望的签名算法（如 `HS256`、`RS256` 等）

#### JWT 结构

JWT 由三部分组成：

```
JWT = base64UrlEncode(Header) + "." + base64UrlEncode(Payload) + "." + Signature
```

- **Header（头部）**：用于描述签名算法和 token 类型，例如：

  ```json
  {
    "alg": "HS256",
    "typ": "JWT"
  }
  ```

- **Payload（负载）**：包含声明信息（即用户或设备的相关数据），例如：

  ```json
  {
    "username": "emqx_user",
    "exp": 1719830400,
    "client_attrs": {
      "role": "admin",
      "sn": "device-001"
    }
  }
  ```

- **Signature（签名）**：用于校验 token 是否被篡改，通过对 header 和 payload 签名生成。

#### 生成步骤

1. 定义 JWT Header。例如（使用 HMAC SHA-256 算法）：

   ```json
   {
     "alg": "HS256",
     "typ": "JWT"
   }
   ```

   若使用 RSA/ECDSA 算法，请将 `"alg"` 替换为 `RS256`、`ES256` 等。

2. 定义 JWT Payload。Payload 中包含 EMQX 使用的声明字段，例如：

   ```json
   {
     "sub": "mqtt_client",       // Subject：可选，用于标识
     "username": "emqx_user",    // 可选：EMQX 可配置绑定验证
     "clientid": "client_123",   // 可选：用于绑定特定客户端 ID
     "exp": 1719830400           // 必填：过期时间（Unix 时间戳）
   }
   ```

   **注意**：

   - `exp` 是必需字段，否则 EMQX 可能拒绝该 JWT。
   - 如配置验证，需在 JWT 中提供 `username` 和/或 `clientid`。
   - 你还可以添加自定义字段，如 `acl`、`client_attrs` 等。

3. 对 Header 和 Payload 进行 Base64Url 编码。可使用手动方式或相关库完成。

4. 签名 JWT。使用你的密钥或私钥对数据进行签名：

   - HMAC 示例（如 `HS256`）：`HMACSHA256(base64Url(header) + "." + base64Url(payload), secret)`
   - RSA/ECDSA 示例（如 `RS256`）：使用私钥和指定算法生成签名。

5. 组装 JWT：将编码后的 Header、Payload、Signature 用点号 `.` 拼接：

   ```
   <header>.<payload>.<signature>
   ```

6. （可选）验证 JWT：建议使用 [jwt.io](https://jwt.io) 或开发工具验证 JWT 的有效性和结构。

#### 使用 Python (`pyjwt`) 示例

```python
import jwt
import datetime

secret = "your_shared_secret"

payload = {
    "username": "emqx_user",
    "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}

token = jwt.encode(payload, secret, algorithm="HS256")
print(token)
```

如使用 `RS256`，请将 `secret` 替换为私钥，并指定算法为 `RS256`。

#### 最终结果

你现在已经获得一个签名后的 JWT，可根据 EMQX 的配置，将其放入 MQTT `CONNECT` 报文的 username 或 password 字段中进行认证。

有关生成更复杂 JWT 用例的详细指南，请参考博客文章：[JWT 认证原理与 JWKS Endpoint 构建指南](https://www.emqx.com/zh/blog/jwt-authentication-and-jwks-endpoint-in-mqtt)。

## 权限列表

如果 JWT 中包含 `acl` 字段，EMQX 将根据该字段指定的权限对客户端进行访问控制。 详情请参考[权限列表（ACL）](https://docs.emqx.com/zh/emqx/latest/access-control/authn/acl.html)。

## 客户端属性

从 EMQX v5.7.0 版本开始，您可以在 JWT Payload 中使用可选的 `client_attrs` 字段设置[客户端属性](https://docs.emqx.com/zh/emqx/latest/client-attributes/client-attributes.html)。请注意，键和值都必须是字符串类型。

示例：

```json
{
  "exp": 1654254601,
  "username": "emqx_u",
  "client_attrs": {
      "role": "admin",
      "sn": "10c61f1a1f47"
  }
}
```

## 认证配置

在部署中点击 **访问控制** -> **客户端认证** -> **扩展认证**，选择 **JWT 认证**，点击**配置认证**。


您可根据如下说明完成相关配置：

如**验证方式**选择 **JWT** 时：

- **JWT 来自于**：指定客户端连接请求中 JWT 的位置；可选值： `password`、 `username`（分别对应于 MQTT 客户端 `CONNECT` 报文中的 `Password` 和 `Username` 字段）

- **加密方式**：指定 JWT 的加密方式，可选值： `hmac-based`、`public-key`；
    - 如选择 `hmac-based`，即 JWT 使用对称密钥生成签名和校验签名（支持 HS256、HS384 和 HS512 算法），还应配置：
      - **Secret**：用于校验签名的密钥，与生成签名时使用的密钥相同。
      - **Secret 使用 Base64 编码**：配置 EMQX 在使用 `Secret` 校验签名时是否需要先对其进行 Base64 解密；可选值：True、False，默认值：False。
    
    - 如选择 `public-key`，即 JWT 使用私钥生成签名，同时需要使用公钥校验签名（支持 RS256、RS384、RS512、ES256、ES384 和 ES512 算法），还应配置：
      - **Public Key**：指定用于校验签名的 PEM 格式的公钥。
    
- **过期后断开连接**：配置是否在 JWT 过期后断开客户端连接，默认启用。

- **Payload**：添加自定义的 Claims 检查；用户需要在 Claim 和 Expected Value 分别添加键和对应的值，支持使用 `${clientid}` 和 `${username}` 占位符。其中键用于查找 JWT 中对应的 Claim，值则用于与 Claim 的实际值进行比较。

如验证方式选择 **JWTS**：

除上述配置外，还应配置：

- **JWKS Endpoint**：指定 EMQX 查询 JWKS 的服务器端点地址，该端点需要支持 GET 请求，并且返回符合规范的 JWKS。
- **JWKS 刷新间隔**：指定 JWKS 的刷新间隔，也就是 EMQX 查询 JWKS 的间隔。默认值：300 单位为秒（s）。
点击创建完成相关配置。
- **请求头**：指定必须包含在对 JWKS 服务器请求中的任何其他 HTTP 请求头。添加这些 HTTP 请求头可以确保对 JWKS 服务器的请求根据服务器的要求进行正确格式化。此配置允许用户添加键值对，例如：
  - **键**：`Accept`
  - **值**：`application/json`

::: tip
* 如果当前部署为专有版，需创建 [VPC 对等连接](../deployments/vpc_peering.md)，服务器地址填写内网地址。
* 如果当前部署为 BYOC 版，需在您的公有云控制台中创建 VPC 对等连接，具体请参考[创建 BYOC 部署 - VPC 对等连接配置](../create/byoc.md#vpc-对等连接配置) 章节。服务器地址填写内网地址。
* 若提示 Init resource failure! 请检查服务器地址是否无误、安全组是否开启。
:::

