Objective-C 签名 SDK 接入指南
功能说明
Objective-C 签名 SDK 用于为美图开放平台 API 请求生成 X-Sdk-Date 和 Authorization 请求头,并返回可由 NSURLSession 直接发送的 NSURLRequest。SDK 使用 SDK-HMAC-SHA256 算法,通过 AK/SK 完成请求签名。
SK 必须保存在服务端,不可写入发布给用户的 iOS、macOS 或其他客户端。
环境要求
- Apple Objective-C 工程
- Foundation
- CommonCrypto
- NSURLSession
- ARC
SDK 未指定最低系统版本,请以业务工程的 deployment target 和编译环境为准。
下载与引入
SDK 版本:1.0.0
下载 Objective-C SDK 与示例
解压后包含:
AIGCP-API-ObjectiveC-sdk-1.0.0/
├── MTSigner.h
├── MTSigner.m
└── MTDemo.m将 MTSigner.h 和 MTSigner.m 添加到工程 Target,在调用处引入:
#import "MTSigner.h"接口说明
初始化签名器
- (instancetype)initWithKey:(NSString *)key
secret:(NSString *)secret;| 参数 | 说明 |
|---|---|
key | Access Key(AK) |
secret | Secret Key(SK) |
创建签名请求
- (NSURLRequest *)signRequest:(NSURL *)url
method:(NSString *)method
headers:(NSDictionary<NSString *, NSString *> *)headers
body:(NSString *)body
error:(NSError **)error;| 参数 | 说明 |
|---|---|
url | 最终请求 URL,包含已经编码和排序的查询参数 |
method | HTTP 方法,例如 GET、POST |
headers | 参与签名的请求头,必须包含 Host |
body | 实际发送的 UTF-8 请求体;GET 传 @"" |
error | 错误输出参数 |
返回值是已经设置请求方法、请求头和 body 的 NSURLRequest。应直接发送该对象,不要在签名后重新创建或修改请求。
请求头常量
| 常量 | 请求头/值 |
|---|---|
kHeaderHost | Host |
kHeaderXDate | X-Sdk-Date |
kHeaderAuthorization | Authorization |
kHeaderContentSha256 | X-Sdk-Content-Sha256 |
kAlgorithm | SDK-HMAC-SHA256 |
若未传 X-Sdk-Date,SDK 会自动生成 UTC 时间戳。签名完成后,SDK 会自动添加 Authorization。
POST 完整调用示例
下面的示例调用正式同步任务接口 https://openapi.meitu.com/api/v1/sdk/sync/push。请求 body 包含五个顶层字段:task、task_type、init_images、作为 JSON 字符串传递的 params,以及 sync_timeout。请根据目标能力的 API 文档替换任务、业务参数、图片和超时值。
#import <Foundation/Foundation.h>
#import "MTSigner.h"
static void SendSignedPOST(void) {
NSDictionary *environment = [NSProcessInfo processInfo].environment;
NSString *accessKey = environment[@"MEITU_OPENAPI_AK"];
NSString *secretKey = environment[@"MEITU_OPENAPI_SK"];
if (accessKey.length == 0 || secretKey.length == 0) {
NSLog(@"Missing MEITU_OPENAPI_AK or MEITU_OPENAPI_SK");
return;
}
NSURL *url = [NSURL URLWithString:
@"https://openapi.meitu.com/api/v1/sdk/sync/push"];
NSDictionary *payload = @{
@"task": @"/v1/replace-with-product-task",
@"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
};
NSError *jsonError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload
options:0
error:&jsonError];
if (jsonData == nil) {
NSLog(@"Failed to serialize request JSON: %@", jsonError);
return;
}
NSString *body = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSDictionary<NSString *, NSString *> *headers = @{
kHeaderHost: @"openapi.meitu.com",
@"Content-Type": @"application/json; charset=UTF-8",
@"Accept": @"application/json"
};
MTSigner *signer = [[MTSigner alloc] initWithKey:accessKey secret:secretKey];
NSError *signError = nil;
NSURLRequest *request = [signer signRequest:url
method:@"POST"
headers:headers
body:body
error:&signError];
if (request == nil || signError != nil) {
NSLog(@"Failed to sign request: %@", signError);
return;
}
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error != nil) {
NSLog(@"Request failed: %@", error);
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSString *responseBody = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(@"HTTP %ld", (long)httpResponse.statusCode);
NSLog(@"%@", responseBody);
}];
[task resume];
}运行前设置环境变量,并把示例中的 task、params、图片信息和 sync_timeout 替换为目标接口要求的值。body 只序列化一次;SDK 使用同一字符串的 UTF-8 字节进行签名并创建 HTTPBody,随后直接发送签名器返回的 NSURLRequest。
GET 调用示例
GET 请求通常不包含 body。所有查询参数应在签名前完成百分比编码和排序,并写入最终 URL。
NSURL *url = [NSURL URLWithString:
@"https://openapi.meitu.com/api/v1/sdk/status?task_id=replace-with-task-id"];
NSDictionary<NSString *, NSString *> *headers = @{
kHeaderHost: @"openapi.meitu.com",
@"Accept": @"application/json"
};
MTSigner *signer = [[MTSigner alloc] initWithKey:accessKey secret:secretKey];
NSError *signError = nil;
NSURLRequest *request = [signer signRequest:url
method:@"GET"
headers:headers
body:@""
error:&signError];
if (request != nil && signError == nil) {
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error != nil) {
NSLog(@"Request failed: %@", error);
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSString *body = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(@"HTTP %ld", (long)httpResponse.statusCode);
NSLog(@"%@", body);
}];
[task resume];
}注意事项
- 使用 HTTPS,并确保
Host与 URL 的主机一致;本域名应填写openapi.meitu.com。 method必须与实际请求一致,并使用大写形式,例如GET、POST。- URL、查询参数、参与签名的请求头和 body 必须在签名前确定,签名后不得修改。
- 查询参数需先按接口签名规则完成 RFC 3986 百分比编码和排序;SDK 不会自动排序查询串。
- POST 签名与发送必须使用完全相同的 UTF-8 body bytes。直接发送 SDK 返回的
NSURLRequest。 - GET 传空字符串
@""作为 body。 - 保持服务器系统时间准确。自动生成的
X-Sdk-Date为 UTC 时间。 - 默认情况下 body 的 SHA-256 参与签名。若目标接口支持不校验 body 摘要,可在签名前设置:
headers[kHeaderContentSha256] = @"UNSIGNED-PAYLOAD";常见错误
| 现象 | 处理方法 |
|---|---|
返回 401、403 或签名无效 | 检查 AK/SK、服务器时间、Host、HTTP 方法,以及签名后 URL、请求头和 body 是否发生变化 |
| POST 验签失败 | 确认 JSON 只序列化一次,并直接发送签名器返回的请求;检查 Content-Type 和实际 UTF-8 body 是否一致 |
| GET 验签失败 | 检查查询参数是否在签名前完成编码和排序,并确认 body 为 @"" |
UNSIGNED-PAYLOAD 不生效 | 使用 kHeaderContentSha256,确认值拼写正确,并确认目标接口支持该方式 |
| 请求超时或无法连接 | 检查 HTTPS、DNS、网络代理和 NSURLSession 超时设置,并查看 completion handler 返回的网络错误 |
| 返回业务参数错误 | 根据目标能力 API 文档核对 task、task_type、init_images、params 和 sync_timeout |