开发指南/消息卡片/配置卡片回调/配置回调请求地址
# 配置回调请求地址

如果消息卡片内配置了用于回传交互的组件，则在构建消息卡片后、发送消息卡片前，你需要在发送消息卡片的应用内配置 **消息卡片请求网址**。后续当用户在消息卡片内操作交互组件时，应用会向该请求网址发送 HTTP POST 请求，请求包含消息卡片交互信息以及用于安全校验的数据信息，便于你在服务器中进一步做业务处理。本文将介绍如何配置卡片回调的请求地址，以及如何对回调请求进行安全校验。warning
该文档为历史消息卡片文档，已不再维护。了解如何配置新版交互，参考[配置卡片交互](https://open.larksuite.com/document/ukTMukTMukTM/uYjNwUjL2YDM14iN2ATN)。
## 操作步骤

1. 登录[开发者后台](https://open.larksuite.com/app)，进入用于发送消息的应用详情页。
2. 进入 **应用能力** > **机器人** 页面。

你需要为应用开启机器人能力，才可以发送消息、配置消息卡片回调。

3. 在 **机器人** 配置页面，点击 **消息卡片回调请求方式** 右侧的编辑按钮。

![image.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/4b21add9d22bdc1e5b02748b118c3e12_24UrMaW6Jl.png?height=1188&lazyload=true&maxWidth=600&width=2214)

4. 在 **消息卡片请求网址** 输入框内填写业务服务器的公网访问地址，并点击 **验证**。

![image.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/c4f06dc9ea20ddd67ad2cd9b3142c57f_2vnhLkfSt4.png?height=588&lazyload=true&maxWidth=600&width=1652)

开放平台会向你配置的请求地址发送一个`application/json`格式的 POST 请求，用于验证你所配置地址的合法性。

请求示例如下：
    ```JSON
    {
      "challenge": "1b6aef1a-401f-406a-be41-f48911e00be7",
      "type": "url_verification",
      "token": "qjSwzC****4T1uhJ"
    }
    ```

参数说明如下：

参数 | 类型 | 描述
---|---|---
challenge | String | 用于验证的字段，需在响应体中原样返回该值。<br>示例值：1b6aef1a-401f-***
type | String | 事件类型。用于校验的事件类型固定取值为 `url_verification`，表示当前请求是在验证 URL 合法性。
token | String | 应用验证标识 **Verification Token**。你可以通过该 Token 验证请求是否属于当前应用。<br>支持在开发者后台 > 应用详情页 > 开发配置 > 事件订阅模块查看应用的 **Verification Token**。

你需要在 1 秒内将 `challenge` 值（JSON 格式）原样返回给Lark开放平台，否则请求地址验证失败。

返回示例如下：

```JSON
{
	"challenge": "1b6aef1a-401f-406a-be41-f48911e00be7",
}
```

5. 验证通过后，点击 **保存** 完成配置。

## 安全校验

消息卡片的安全校验步骤为可选步骤，你可以通过安全校验判断请求是否来自Lark开放平台，并且还可以防止被重放攻击。

### 防止重复发送回调请求

在网络抖动等极端情况下，可能会出现卡片点击失败但是该交互动作已被处理的情况，进而可能导致重复发送该回调事件的情况。你可以通过验证 `headers['X-Refresh-Token']` 来防止点击事件被重复处理。

只有在卡片的点击事件成功被响应，并通知到客户端的时候才会刷新 `X-Refresh-Token`，如果你用于处理回调事件的接口非幂等，可以通过缓存并验证该字段防止接口被重复调用。

### 签名校验

在消息卡片请求地址收到回调请求时，支持使用 **Verification Token** 进行签名验证，以验证该请求是否来自Lark开放平台。
目前仅支持通过 Verification Token 判断请求是否来自Lark开放平台，不支持使用应用的 Encrypt Key 进行签名校验。

签名校验步骤如下：

1. 登录[开发者后台](https://open.larksuite.com/app)。
2. 进入指定应用详情页。
3. 在 **开发配置** > **事件订阅** 页面，获取应用的 **Verification Token** 值。

![图片](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/cf0671eec8da256b345f4b6a42820614_MGUYDngZ61.png?height=660&lazyload=true&maxWidth=600&width=2230)

4. 获取请求头中的 `X-Lark-Request-Timestamp`、`X-Lark-Request-Nonce` 值，分别记为 `timestamp`、`nonce`。
5. 获取原始请求 Body 的数据，记为 `body`。
6. 按 `timestamp`、`nonce`、`Verification Token`、`body` 的顺序进行拼接，并按照 `encode('utf-8')` 编码得到 `byte[] b1`。
7. 对 `byte[] b1` 进行 **SHA-1** 加密得到 `bs`，然后将 `bs` 编码成 16 进制字符串记为 `sig`。
8. 校验 `sig` 与请求头中 `X-Lark-Signature` 的值是否一致。

一致则表示当前请求来自Lark开放平台。

### 签名校验示例代码

以下示例代码中，包括的参数如下：

* `timestamp`：对应请求头中的 `X-Lark-Request-Timestamp`。
* `nonce`：对应请求头中的 `X-Lark-Request-Nonce`。
* `token`：从开发者后台获取的 **Verification Token**。

**Go 代码示例**

```Go
// 创建签名
func GenPostRequestSignature(nonce string, timestamp string, body string, token string) string {
   var b strings.Builder
   b.WriteString(timestamp)
   b.WriteString(nonce)
   b.WriteString(token)
   b.WriteString(body)
   bs := []byte(b.String())
   h := sha1.New()
   h.Write(bs)
   bs = h.Sum(nil)
   return fmt.Sprintf("%x", bs)
}
```

**Python 3 代码示例**

```Python
import hashlib
bytes_b1 = (timestamp + nonce + token).encode('utf-8')
b = bytes_b1 + body
h = hashlib.sha1(b)
sig = h.hexdigest()
```

**Java 代码示例**

```Java
import org.apache.commons.codec.binary.Hex;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

class Main {
    public String calculateSignature(String timestamp, String nonce, String token, String body) throws NoSuchAlgorithmException {
        StringBuilder content = new StringBuilder();
        content.append(timestamp).append(nonce).append(token).append(body);
        MessageDigest alg = MessageDigest.getInstance("SHA-1");
        String sig = Hex.encodeHexString(alg.digest(content.toString().getBytes()));
        return sig;
    }
}
```
