Kotlin 签名 SDK 接入指南

功能说明

Kotlin 签名 SDK 用于为美图开放平台 API 请求生成以下认证请求头:

  • X-Sdk-Date:UTC 请求时间。
  • Authorization:基于 AK/SK 和 SDK-HMAC-SHA256 算法生成的签名。

SDK 支持 GET、POST 等 HTTP 方法,并提供同步 HTTP 调用接口和基于协程的异步调用方式。SK 只应保存在受信任的服务端,不要写入 Android 或其他客户端应用。

环境要求

  • JDK 8 或更高版本。
  • Kotlin 1.5 或更高版本。
  • 使用 MTHttpClient.ktMTDemo.kt 时,需要添加 kotlinx-coroutines-core
  • SDK 下载包为 Kotlin 源码包,需要将源码复制到 Kotlin/JVM 服务端工程中使用。

下载与引入

下载 Kotlin SDK 1.0.0 与 Demo

解压后包含:

AIGCP-API-kotlin-sdk-1.0.0/
├── MTSigner.kt
├── MTHttpClient.kt
├── MTDemo.kt
└── README.md

MTSigner.ktMTHttpClient.kt 复制到工程的以下目录:

src/main/kotlin/com/meitu/signer/

在 Gradle 工程中添加协程依赖。以下版本可用于 Kotlin 1.9 工程;已有工程也可以使用其统一管理的兼容版本:

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
}

业务代码中的导入方式:

import com.meitu.signer.MTHttpClient
import com.meitu.signer.MTSigner
import com.meitu.signer.SignatureInfo

接口说明

创建签名器

val signer = MTSigner(accessKey, secretKey)
参数说明
accessKey应用的 API Key(AK)
secretKey应用的 Secret Key(SK)

生成签名

fun sign(
    url: String,
    method: String,
    headers: Map<String, String>,
    body: String = ""
): SignatureInfo
参数说明
url完整请求 URL,包含路径和查询参数
methodHTTP 方法,使用大写形式,例如 GETPOST
headers待签名请求头;必须包含 Host
body实际发送的请求体;GET 请求通常使用空字符串

返回值:

data class SignatureInfo(
    val url: String,
    val method: String,
    val headers: Map<String, String>,
    val body: String?
)

headers 中包含 SDK 生成的 X-Sdk-DateAuthorization,发送请求时必须使用返回对象中的 URL、方法、请求头和 body。

HTTP 调用

同步调用:

val client = MTHttpClient.createDefault()
val response = client.execute(signedInfo)

协程调用:

val client = MTHttpClient.createDefault()
val response = with(MTHttpClient.Companion) {
    client.executeAsync(signedInfo)
}

响应结构:

data class Response(
    val code: Int,
    val headers: Map<String, List<String>>,
    val body: String?
)

不校验 body 摘要

默认情况下,body 的 SHA-256 摘要会参与签名。如果目标接口允许使用 UNSIGNED-PAYLOAD,请在签名前设置:

headers[MTSigner.HEADER_CONTENT_SHA256] = "UNSIGNED-PAYLOAD"

POST 完整调用示例

以下示例调用正式任务提交接口:

https://openapi.meitu.com/api/v1/sdk/sync/push

请求 body 包含 tasktask_typeinit_imagesparamssync_timeout 五个顶层字段,params 为 JSON 字符串。示例中的任务、业务参数、图片地址和超时时间需要按目标能力 API 文档替换。

package example

import com.meitu.signer.MTSigner
import com.meitu.signer.MTHttpClient

suspend fun main() {
    val signer = MTSigner(
        requireEnv("MEITU_OPENAPI_AK"),
        requireEnv("MEITU_OPENAPI_SK")
    )
    val url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
    val body = """
        {
          "task": "/v1/Text_Chart_High_Definition/472492",
          "task_type": "formula",
          "init_images": [
            {
              "url": "https://example.com/input.jpg",
              "profile": {
                "media_profiles": {
                  "media_data_type": "url"
                },
                "version": "v1"
              }
            }
          ],
          "params": "{\"parameter\":{\"rsp_media_type\":\"url\"}}",
          "sync_timeout": 30
        }
    """.trimIndent()
    val headers = mapOf(
        MTSigner.HEADER_HOST to "openapi.meitu.com",
        "Content-Type" to "application/json; charset=UTF-8"
    )

    val signedInfo = signer.sign(url, "POST", headers, body)
    val client = MTHttpClient.createDefault()
    val response = with(MTHttpClient.Companion) {
        client.executeAsync(signedInfo)
    }

    println("HTTP ${response.code}")
    println(response.body.orEmpty())
}

private fun requireEnv(name: String): String {
    return requireNotNull(System.getenv(name)) {
        "Missing environment variable: $name"
    }
}

示例将签名后返回的 signedInfo 直接交给 HTTP 客户端,确保发送内容与签名内容一致。

GET 调用示例

以下示例查询任务状态。将该函数与 POST 示例中的 requireEnv 放在同一文件中即可使用:

suspend fun queryTask(taskId: String) {
    val signer = MTSigner(
        requireEnv("MEITU_OPENAPI_AK"),
        requireEnv("MEITU_OPENAPI_SK")
    )
    val url = "https://openapi.meitu.com/api/v1/sdk/status?task_id=$taskId"
    val headers = mapOf(
        MTSigner.HEADER_HOST to "openapi.meitu.com",
        "Accept" to "application/json"
    )

    val signedInfo = signer.sign(url, "GET", headers)
    val client = MTHttpClient.createDefault()
    val response = with(MTHttpClient.Companion) {
        client.executeAsync(signedInfo)
    }

    println("HTTP ${response.code}")
    println(response.body.orEmpty())
}

任务 ID 如果包含特殊字符,应先进行 URL 编码,再构造并签名 URL。

注意事项

  • Host 必须设置为 openapi.meitu.com,不能包含协议或路径。
  • URL、HTTP 方法、查询参数、已签名请求头和 body 在签名后不得修改。
  • POST 请求必须使用签名时的同一 body,并按 UTF-8 字节发送。
  • params 必须是 JSON 字符串,不是嵌套 JSON 对象。
  • tasktask_type、图片字段和 sync_timeout 以目标能力 API 文档为准。
  • 默认让 SDK 生成 X-Sdk-Date;调用服务器需要保持系统时间准确。
  • 内置发送器按 JVM 默认字符集写入 body;body 含非 ASCII 字符时,必须确保 JVM 默认字符集为 UTF-8。
  • 下载包内置发送器基于阻塞式 HTTP;服务端应在 I/O 线程或协程 Dispatchers.IO 中调用并设置超时。

常见错误

现象处理方法
HTTP 401 或签名校验失败检查 AK/SK、服务器时间、Host,并确认签名后没有修改 URL、请求头或 body
HTTP 400 或业务参数错误确认 body 是合法 JSON,params 已序列化为字符串,业务字段符合目标能力文档
kotlinx.coroutines 无法解析添加与工程 Kotlin 版本兼容的 kotlinx-coroutines-core 依赖
GET 签名失败在签名前完成查询参数编码,签名后不要改变参数顺序或编码形式
含中文的 body 验签失败确认签名和发送使用完全相同的 UTF-8 body 字节
请求超时或无响应设置连接超时和读取超时,并检查网络、DNS 和接口地址