0

Мне поручено внедрить iOS-запрос на основе объектной системы Azure DocumentDB с использованием схемы API REST. Используя код, найденный в github, в частности https://github.com/Azure/azure-storage-ios, я смог создать запрос, который надлежащим образом проверяет подлинность и возвращает соответствующие данные ... иногда.Ошибка Azure DocumentDB Intermittent 401 при запросе API REST через Obj-c

Проблема: я получаю сообщение об ошибке с ошибкой 401 (аутентификация) с сервера с перерывами. Выполнение такого же запроса через Node.js не вызывает такого поведения, поэтому я считаю, что это проблема с моей реализацией цели-c.

- (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 и смог разрешить любую помощь, это будет оценено по достоинству. Или предложения по отладке шагов для вышеуказанного кода, учитывая, что я попытался уменьшить временную метку на несколько секунд, аналогичные прерывистые сбои.

Хотя при повторном повторении нескольких попыток повторения нескольких секунд при ответе 200 ответов в 1-2 повторениях, я не чувствую, что это идеальное решение любым способом.

Спасибо за ваше время.

Обновление: Пожалуйста, объясните объяснение Эндрю Лю, приведенное ниже, по причине этой ошибки. Я отметил его ответ как ответ, ниже - обновленный фрагмент кода.

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; 

ответ

1

401 (auth failure) обычно указывает на то, что что-то не так с токеном auth.

Важно отметить, что токен auth представляет собой кодировку Base64, что означает, что она может содержать символ +.

Сервер db ожидает, что символы в токене аутентификации будут закодированы в + (%2B) ... некоторые, но не все HTTP-клиенты будут автоматически кодировать HTTP-заголовки для вас.

Я подозреваю, что URL-кодирование или преобразование + в %2B для следующей переменной зафиксирует ваш прерывистый 401 вопрос:

NSString* signature = [HMACData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; 
+0

Это отличный улов, и именно эта проблема, все неудачи аутентификации токены содержали +, который Я не был правильно преобразован. Я принял ответ и спасибо Эндрю! – Glorifundel

0

Я видел этот вопрос раньше, и, как правило, это связано с последними 2 шага протокола, т.е. либо кодирование base64 является эксцентричный, или кодирование URI. Одним из способов отладки этого было бы напечатать маркер аутентификации, который вы отправили, в случае сбоя, и посмотреть, есть ли какие-либо странные символы, которые, возможно, не передаются правильно. Здесь вы можете опубликовать токен багги, и я могу посмотреть.