通过Obj-c查询REST API时出现Azure DocumentDB Intermittent 401错误 [英] Azure DocumentDB Intermittent 401 error when querying REST API via Obj-c

查看:163
本文介绍了通过Obj-c查询REST API时出现Azure DocumentDB Intermittent 401错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我负责使用REST API方案实现Azure DocumentDB系统的基于Objective-c的iOS查询。利用github上的代码,特别是 https://github.com/Azure/azure-storage-ios 我能够生成一个适当的身份验证并返回相应数据的请求....有时候。

I've been charged with implementing an objective-c based iOS query of the Azure DocumentDB system using the REST API scheme. Utilizing the code found on github, specifically https://github.com/Azure/azure-storage-ios I was able to generate a request that appropriately authenticates and returns the appropriate data.... sometimes.

问题:我收到401(身份验证失败) )间歇性地从服务器响应错误。通过Node.js发出相同的请求不会遇到这种情况,所以我认为这是我的objective-c实现的问题。

The problem: I receive a 401 (authentication failure) error response from the server intermittently. Making the same request via Node.js does not encounter this behavior, so I believe this to be an issue with my objective-c implementation.

- (NSMutableURLRequest *) RequestWithQuery:(NSString*)query Parameters:(NSArray*)parameters {

NSError* error;
NSDictionary* dictionaryOfBodyContents = @{@"query":query,
                                           @"parameters":parameters};
NSData* body = [NSJSONSerialization dataWithJSONObject:dictionaryOfBodyContents
                                               options:NSJSONWritingPrettyPrinted
                                                 error:&error];

if(error != nil) {
    NSLog(@"AzureRequestWithQueryParameters error generating the body: %@",error);
    return nil;
}

char buffer[30];
struct tm * timeptr;

time_t time = (time_t) [[NSDate date] timeIntervalSince1970];
timeptr = gmtime(&time);
if (!strftime_l(buffer, 30, [@"%a, %d %b %Y %T GMT" UTF8String], timeptr, NULL))
{
    NSException* myException = [NSException
                                exceptionWithName:@"Error in date/time format"
                                reason:@"Unknown"
                                userInfo:nil];
    @throw myException;
}
NSString* date = [NSString stringWithUTF8String:buffer];
// generate auth token
NSString* authorizationToken = [self AuthorizationTokenForTableQueryWithDate:date];

// generate header contents
NSDictionary* dictionaryOfHeaderContents = @{@"authorization":authorizationToken,
                                             @"connection":AZURE_CONNECTION_HEADER_CONNECTION,
                                             @"content-type":AZURE_CONNECTION_HEADER_CONTENTTYPE,
                                             @"content-length":[NSString stringWithFormat:@"%lu",(unsigned long)[body length]],
                                             @"x-ms-version":AZURE_CONNECTION_APIVERSION,
                                             @"x-ms-documentdb-isquery":@"true",
                                             @"x-ms-date":date.lowercaseString,
                                             @"cache-control":@"no-cache",
                                             @"user-agent":AZURE_CONNECTION_HEADER_USERAGENT,
                                             @"accept":@"application/json"};

// generate url contents
NSString* urlString = [NSString stringWithFormat:@"https://%@:%@/%@", AZURE_URL_HOST, AZURE_URL_PORT, AZURE_URL_DOCUMENTS];
NSURL* url = [NSURL URLWithString:urlString];

NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:AZURE_CONNECTION_METHOD];
[request setAllHTTPHeaderFields:dictionaryOfHeaderContents];
[request setHTTPBody:body];
return request;
}

- (NSString*) AuthorizationTokenForTableQueryWithDate:(NSString*)date {
//
//  Based on https://msdn.microsoft.com/en-us/library/azure/dd179428.aspx under "Table Service (Shared Key Authentication)"
//
//    generating a authentication token is a Hash-based Message Authentication Code (HMAC) constructed from the request
//      and computed by using the SHA256 algorithm, and then encoded by using Base64 encoding.
//
//    StringToSign =  VERB + "\n" +
//                    Content-MD5 + "\n" +
//                    Content-Type + "\n" +
//                    Date + "\n" +
//                    CanonicalizedHeaders +
//                    CanonicalizedResource;
//
NSString* StringToSign = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n\n",
                          AZURE_CONNECTION_METHOD.lowercaseString?:@"",
                          AZURE_RESOURCE_TYPE.lowercaseString?:@"",
                          AZURE_URL_COLLECTIONS.lowercaseString?:@"",
                          date.lowercaseString?:@""];

// Generate Key/Message pair
NSData* keyData = [[NSData alloc] initWithBase64EncodedString:AZURE_AUTH_KEY options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSData* messageData = [StringToSign dataUsingEncoding:NSUTF8StringEncoding];

// Encrypt your Key/Message using HMAC SHA256
NSMutableData* HMACData = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, keyData.bytes, keyData.length, messageData.bytes, messageData.length, HMACData.mutableBytes);

// Take your encrypted data, and generate a token that Azure likes.
NSString* signature = [HMACData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString* unencodedToken = [NSString stringWithFormat:@"type=master&ver=1.0&sig=%@",signature];
NSString* authorizationToken = [unencodedToken stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
authorizationToken = [authorizationToken stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"];

return authorizationToken;
}

如果有人遇到类似的间歇性401并能够解决任何帮助不胜感激。或者考虑到上述代码调试步骤的建议,我尝试将时间戳减少几秒钟,类似的间歇性故障。

If anyone has encountered a similar intermittent 401 and was able to resolve any help would be appreciated. Or suggestions for debugging steps for the above code bearing in mind, I have attempted decrementing the timestamp by a few seconds, similar intermittent failures.

虽然只是重试了几次在减少秒数时失败导致1-2次重试产生200响应,我觉得无论如何都不是理想的解决方案。

Although simply retrying a few times upon a failure while decrementing the seconds results in a 200 response in 1-2 retries, I don't feel it is an ideal solution by any means.

谢谢你的支持时间。

更新:请参阅Andrew Liu在下面的解释,了解这次失败的原因。我已将其响应标记为答案,下面是更新的代码片段。

Update: Please see Andrew Liu's explanation below for the reason for this failure. I have flagged his response as the answer, below is the updated snippet of code.

NSString* unencodedToken = [NSString stringWithFormat:@"type=master&ver=1.0&sig=%@",signature];
//    NSString* authorizationToken = [unencodedToken stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
//    authorizationToken = [authorizationToken stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"];
NSString* authorizationToken = [unencodedToken stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@"&+="] invertedSet]];
return authorizationToken;


推荐答案

401(auth failure)通常表示出现问题使用身份验证令牌。

401 (auth failure) usually indicates that something is wrong with the auth token.

请注意,身份验证令牌是Base64编码的字符串 - 这意味着它可以包含 + character。

It's important to note that the auth token is a Base64-encoded string - meaning it can contain the + character.

数据库服务器期望auth令牌中的 + 字符进行url编码(%2B )...部分但并非所有HTTP客户端都会自动为您编码HTTP标头。

The db server expects + characters in the auth token to be url encoded (%2B)... some but not all HTTP clients will automatically encode HTTP headers for you.

我怀疑网址 - 将 + 编码或转换为%2B 以获取以下变量将解决您的间歇性401问题:

I suspect url-encoding or converting + to %2B for the following variable will fix your intermittent 401 issue:

NSString* signature = [HMACData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

这篇关于通过Obj-c查询REST API时出现Azure DocumentDB Intermittent 401错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆