# 基于 HTTP 应用进行授权

::: tip
从 EMQX v5.8.0 开始，HTTP 认证器支持在响应体中包含权限列表 (ACL) 规则用来为客户端预设权限。建议您使用新格式以获得更好的性能。有关详细信息，请参见 [HTTP 认证](../authn/http.md)。
:::

EMQX 支持基于 HTTP 应用进行授权。此时，用户需在外部自行搭建一个 HTTP 应用作为数据源，EMQX 将向 HTTP 服务发起请求并根据 HTTP API 返回的数据判定授权结果，从而实现复杂的授权逻辑。

在 4.x 版本中，EMQX 仅会提示 HTTP API 返回的状态码，如  `200` 、`403`，内容则会被丢弃。为了向用户提供更多的信息，我们在 EMQX 5.0 版本中增加了对请求内容的返回。

::: tip 前置准备

熟悉 [EMQX 授权基本概念](./authz.md)

:::

## 通过 Dashboard 配置

在 [EMQX Dashboard](http://127.0.0.1:18083/#/authentication) 页面，点击左侧导航栏的**访问控制** -> **授权**，在随即打开的**授权**页面，单击**创建**，选择**数据源**为 `HTTP Server`，点击**下一步**，进入**配置参数**页签：

<img src="./assets/authz-http.png" alt="HTTP authorization" style="zoom:67%;" />

**HTTP**：<!--插入简要说明，这快要配置什么-->

- **请求方式**：选择 HTTP 请求方式，可选值： `get` 、 `post`

  ::: tip
  推荐使用 `POST` 方法。 使用 `GET` 方法时，一些敏感信息（如纯文本密码）可能通过 HTTP 服务器日志记录暴露。此外，对于不受信任的环境，请使用 HTTPS。
  :::

- **URL**：输入 HTTP 应用的 IP 地址。

- **请求头**（可选）：完成 HTTP 请求头的配置。键和值支持使用[占位符](./authz.md#占位符)。

**连接配置**：在此部分进行并发连接、连接超时等待时间、最大 HTTP 请求数以及请求超时时间。

- **连接池大小**（可选）：整数，指定从 EMQX 节点到外部 HTTP Server 的并发连接数；默认值：**8**。<!--有范围吗？-->
- **连接超时**（可选）：填入连接超时等待时长，可选单位：**小时**、**分钟**、**秒**、**毫秒**。
- **HTTP 管道**（可选）：正整数，指定无需等待响应可发出的最大 HTTP 请求数；默认值：**100**。
- **请求超时**（可选）：填入连接超时等待时长，可选单位：**小时**、**分钟**、**秒**、**毫秒**

**TLS 配置**：配置是否启用 TLS。

**请求体**：在此处完成 HTTP 请求体的配置。

<!--需要补上相关信息-->

最后点击**创建**完成相关配置。


## 请求格式与返回结果

当客户端发起订阅、发布操作时，HTTP Authorizer 会根据配置的请求模板构造并发送请求到外部 Web 服务（授权服务）。用户需要在授权服务中实现授权检查逻辑并按要求返回结果，EMQX 根据响应结果判断是否具备权限。

### 请求格式

根据授权服务要求而定，可以使用 JSON 格式，支持在 URL 与请求体中使用以下占位符：

- `${clientid}`: 客户端的 ID。
- `${username}`: 客户端登录时用的用户名。
- `${client_attrs.NAME}`：某个客户端属性。`NAME` 将在运行时根据预定义配置替换为属性名称。有客户端属性的详细信息，请参见 [MQTT 客户端属性](../../client-attributes/client-attributes.md)。
- `${peerhost}`: 客户端的源 IP 地址。
- `${proto_name}`: 客户端使用的协议名称。例如 `MQTT`，`CoAP` 等。
- `${mountpoint}`: 网关监听器的挂载点（主题前缀）。
- `${action}`: 当前执行的动作请求，例如 `publish`， `subscribe`。
- `${topic}`: 当前请求想要发布或订阅的主题（或主题过滤器）。
- `${qos}`: 当前请求想要发布或订阅的消息 QoS。
- `${retain}`: 当前请求想要发布的消息是否为保留消息。
- `${zone}`: 客户端在运行时的 Zone。Zone 是对客户端的一种逻辑分类，例如区域或环境，可以根据客户端的配置动态应用。


### 响应格式

授权服务完成检查后，返回符合以下要求的响应：

- 响应编码格式 `content-type` 必须是 `application/json`。
- 如果返回的 HTTP 状态码为 `200`，认证结果通过 Body 中的 `result` 标示，可选值为：
  - `allow`：允许发布或订阅
  - `deny`：禁止发布或订阅
  - `ignore`： 忽略请求，移交下一个认证器以继续执行认证链
- 如果返回的 HTTP 状态码为 `204`，认证结果标示为允许发布或订阅。
- 除了 `200` 和 `204` 以外的其他 HTTP 状态码均标示为 ignore，比如 HTTP 服务不可用。  

响应示例：

```json
HTTP/1.1 200 OK
Headers: Content-Type: application/json
...
Body:
{
    "result": "allow" | "deny" | "ignore" // Default `"ignore"`
}
```

::: tip EMQX 4.x 兼容性说明
在 4.x 中，EMQX 仅用到了 HTTP API 返回的状态码，而内容则被丢弃。例如 `200` 表示 `allow`，`403` 表示 `deny`。

因为缺乏丰富的表达能力，在 5.0 中对这一机制进行了不兼容的调整。
:::

::: tip
推荐使用 `POST` 方法。 使用 `GET` 方法时，一些敏感信息可能通过 HTTP 服务器日志记录暴露。
对于不受信任的环境，应使用 HTTPS。
:::

## 配置项

支持 HTTP `POST` 和 `GET` 请求，它们各自都有一些特定的选项。<!--详细配置请参考  [authz:http_post](../../configuration/configuration-manual.html#authz:http_post)与 [authz:http_get](../../configuration/configuration-manual.html#authz:http_get)。-->

HTTP 授权必需使用 `type=http`的配置。

使用 POST 请求配置的示例：

```hcl
{
    type = http

    method = post
    url = "http://127.0.0.1:8080/authz?clientid=${clientid}"
    body {
        username = "${username}"
        topic = "${topic}"
        action = "${action}"
    }
    headers {
        "Content-Type" = "application/json"
        "X-Request-Source" = "EMQX"
    }
}
```

使用 GET 请求配置的示例：

```hcl
{
    type = http

    method = get
    url = "http://127.0.0.1:8080/authz"
    body {
        clientid = "${clientid}"
        username = "${username}"
        topic = "${topic}"
        action = "${action}"
    }
    headers {
        "X-Request-Source" = "EMQX"
    }
}
```

### method

该配置为必填字段，用于指定 http 方法，可以是 `get` 或者 `post`。

### url

发送 HTTP 请求的 URL，可以使用如下[占位符](./authz.md#数据查询占位符):

如果 URL 为 `https`，必须同时启用 `ssl`：

```hcl
{
    ...
    url = "https://127.0.0.1:8080/authz?clientid=${clientid}"
    ssl {
        enable = true
    }
}

```

### body

请求模板，对于 `post` 请求，它以 JSON 形式在请求体中发送。
对于 `get` 请求，它被编码为 URL 中的查询参数。映射键和值可以包含 [占位符](./authz.md#数据查询占位符).

根据配置项的不同 `body` 的序列化方式也可能不同。

假设一个 MQTT 客户端使用客户端标识符 `emqx_c`、用户名 `emqx_u` 向 `t/1` 主题发布消息。

1. `GET` 请求配置如下：

```hcl
{
    method = get
    url = "http://127.0.0.1:8080/authz/${clientid}"
    body {
        username = "${username}"
        topic = "${topic}"
        action = "${action}"
    }
}
```

最终的 HTTP 请求会是下面这样：

```bash
GET /authz/emqx_c?username=emqx_u&topic=t%2F1&action=publish HTTP/1.1
... Headers ...
```

2. `POST` JSON 格式的请求配置如下：

```hcl
{
    method = post
    url = "http://127.0.0.1:8080/authz/${clientid}"
    body {
        username = "${username}"
        topic = "${topic}"
        action = "${action}"
    }
    headers {
        "content-type": "application/json"
    }
}
```

最终的 HTTP 请求会是下面这样：

```bash
POST /authz/emqx_c HTTP/1.1
Content-Type: application/json
... Other headers ...

{"username":"emqx_u","topic":"t/1", "action": "publish"}
```

### headers

配置 HTTP 授权请求中的 Headers，可选。

对于 `GET` 请求有以下默认 Headers：

```hcl
{
    "accept" = "application/json"
    "cache-control" = "no-cache"
    "connection" = "keep-alive"
    "keep-alive" = "timeout=30, max=1000"
}
```

`GET` 请求的 Headers 不能包含 `content-type` Header。

对于 `POST` 请求有以下默认 Headers：

```hcl
{
    "accept" = "application/json"
    "cache-control" = "no-cache"
    "connection" = "keep-alive"
    "keep-alive" = "timeout=30, max=1000"
    "content-type" = "application/json"
}
```

`content-type` Header 的值定义了 `POST` 请求的 `body` 编码方式，目前仅支持 `application/json`。

### enable_pipelining

正整数，用于指定可以无需等待响应发出的 HTTP 请求的最大数量 [HTTP pipelining](https://wikipedia.org/wiki/HTTP_pipelining)。可选，默认值为 `100`，设置为 `1` 表示关闭 HTTP pipelining 功能，即恢复成常规的同步请求响应模式。

### 请求配置

以下都是可选字段，

```hcl
  connect_timeout = 15s # 连接超时
  max_retries = 5 # 最大重试次数
  request_timeout = 30s # 请求超时限制
  retry_interval = 1s # 重试中间间隔
```

### pool_size

可选的整型配置，用于指定 EMQX 节点到 HTTP 服务器的并发连接数，默认值为 8。

### ssl

用于连接到外部 HTTP 服务器的标准 SSL 选项。<!--(../../configuration/configuration.md#tls-ciphers)。-->
