Google App Engine с интерфейсом ClientLogin для Objective-C
Я столкнулся с той же проблемой в этом предыдущем посте stackru.com.
В частности, мне кажется, что я могу правильно получить токен "Auth", но попытка использовать его в заголовке при доступе к более поздним страницам все равно просто возвращает мне HTML-код страницы входа.
Перейдя по ссылкам, связанным с этим сообщением, я решил, что вам нужно сделать следующий звонок по этому URL.
После обращения к URL-адресу вы получите файл cookie ACSID, который затем необходимо будет передать в последующих вызовах, чтобы сохранить аутентифицированное состояние.
Когда я запрашивал этот файл cookie, я читал различные посты, в которых говорилось, что вам нужно указать свой оригинальный токен авторизации, добавив его в строку запроса так, чтобы:
?auth=this_is_my_token
Я также читал, что вы должны установить его в заголовке http, как описано в документации Google, чтобы имя / значение заголовка http было:
Authorization: GoogleLogin auth=yourAuthToken
Я испробовал оба подхода и не вижу никаких файлов cookie. Я использовал Wireshark, LiveHttpHeaders для Firefox и простые операторы NSLog, пытаясь увидеть, возвращено ли что-нибудь подобное.
Ниже приведен фрагмент кода, который я использовал.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://yourapp.appspot.com/_ah/login?auth=%@", [token objectForKey:@"Auth"]]];
NSHTTPURLResponse* response;
NSError* error;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:[NSString stringWithFormat:@"GoogleLogin auth=%@", [token objectForKey:@"Auth"]] forHTTPHeaderField:@"Authorization"];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//show me all header fields
NSLog([[response allHeaderFields] description]);
//show me the response
NSLog(@"%@", [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:@"http://yourapp.appspot.com/_ah/login"]];
//show me all cookies
for (NSHTTPCookie *cookie in all)
{
NSLog(@"Name: %@ : Value: %@", cookie.name, cookie.value);
}
Я надеюсь, что вы можете использовать ClientLogin
для кода Google App Engine.
7 ответов
Добавление примера кода к этому вопросу, потому что кто-то связался со мной напрямую о моем решении. Обратите внимание, что вы должны установить параметр "service" равным "ah" в начальном запросе токена.
Первоначальный запрос токена [выполняется синхронно] ПРИМЕЧАНИЕ: для параметра "service" установлено значение "ah", а для "source" просто установлено значение "myapp", вам следует использовать имя вашего приложения.
//create request
NSString* content = [NSString stringWithFormat:@"accountType=HOSTED_OR_GOOGLE&Email=%@&Passwd=%@&service=ah&source=myapp", [loginView username].text, [loginView password].text];
NSURL* authUrl = [NSURL URLWithString:@"https://www.google.com/accounts/ClientLogin"];
NSMutableURLRequest* authRequest = [[NSMutableURLRequest alloc] initWithURL:authUrl];
[authRequest setHTTPMethod:@"POST"];
[authRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
[authRequest setHTTPBody:[content dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* authResponse;
NSError* authError;
NSData * authData = [NSURLConnection sendSynchronousRequest:authRequest returningResponse:&authResponse error:&authError];
NSString *authResponseBody = [[NSString alloc] initWithData:authData encoding:NSASCIIStringEncoding];
//loop through response body which is key=value pairs, seperated by \n. The code below is not optimal and certainly error prone.
NSArray *lines = [authResponseBody componentsSeparatedByString:@"\n"];
NSMutableDictionary* token = [NSMutableDictionary dictionary];
for (NSString* s in lines) {
NSArray* kvpair = [s componentsSeparatedByString:@"="];
if ([kvpair count]>1)
[token setObject:[kvpair objectAtIndex:1] forKey:[kvpair objectAtIndex:0]];
}
//if google returned an error in the body [google returns Error=Bad Authentication in the body. which is weird, not sure if they use status codes]
if ([token objectForKey:@"Error"]) {
//handle error
};
Следующим шагом будет запуск вашего приложения на Google App Engine, чтобы дать вам файл cookie ASCID. Я не уверен, почему существует этот дополнительный шаг, похоже, это проблема на стороне Google и, возможно, почему GAE в настоящее время нет в их списке obj-c Google Data API API. Мои тесты показывают, что я должен запросить cookie для синхронизации с GAE. Кроме того, обратите внимание, что я ничего не делаю с cookie. Кажется, что, просто запросив его и получив готовый файл, будущие запросы будут автоматически содержать cookie. Я не уверен, что это приложение для iPhone, потому что мое приложение - приложение для iPhone, но я не до конца понимаю, что происходит с этим файлом cookie. ПРИМЕЧАНИЕ: использование "myapp.appspot.com".
NSURL* cookieUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://myapp.appspot.com/_ah/login?continue=http://myapp.appspot.com/&auth=%@", [token objectForKey:@"Auth"]]];
NSLog([cookieUrl description]);
NSHTTPURLResponse* cookieResponse;
NSError* cookieError;
NSMutableURLRequest *cookieRequest = [[NSMutableURLRequest alloc] initWithURL:cookieUrl];
[cookieRequest setHTTPMethod:@"GET"];
NSData* cookieData = [NSURLConnection sendSynchronousRequest:cookieRequest returningResponse:&cookieResponse error:&cookieError];
Наконец, я могу опубликовать JSON в моем приложении Gae. ПРИМЕЧАНИЕ: приведенный ниже фрагмент является асинхронным запросом. Мы можем обработать ответы, реализовав didReceiveResponse, didReceiveData, didFailWIthError.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myapp.appspot.com/addRun?auth=%@", mytoken]];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:@"my http body";
NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!connectionResponse) {
NSLog(@"Failed to submit request");
} else {
NSLog(@"Request submitted");
}
Проверьте код, который делает это в официальном SDK. В последнем выпуске SDK он даже разбит на отдельные файлы.
1-й - спасибо за отличный пост, он действительно начал меня.
2-й - я подключал его к своему приложению, пытаясь отправить POST в GAE во время аутентификации.
Это запрос строится при POSTing, после того как вы приобрели authtoken:
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"image/png" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[request setValue:authtoken forHTTPHeaderField:@"auth"]; // <-- the magic
- mattb
Я создал несколько классов obj-c для реализации ClientLogin, включая поддержку Google App Engine:
Использование HOSTED_OR_GOOGLE является неправильным, и я объясню почему.
В мире Google существует два вида учетных записей. Те, что вы создаете для GMail и т. Д., Являются аккаунтами Google. Те, которые вы создаете для приложений для доменов, являются "размещенными" учетными записями. Вы можете использовать электронную почту размещенной учетной записи, чтобы создать учетную запись Google, создавая адрес электронной почты, который связан с обоими типами учетных записей.
Ваше приложение Google App Engine можно настроить для работы с (1) учетными записями Google или (2) размещенными учетными записями для определенного домена.
Предположим, что мы разрабатываем приложение для учетных записей Google. Пользователь вводит адрес электронной почты, связанный с учетной записью Google и размещенной учетной записью. Google будет использовать свою учетную запись Google для входа в систему. Это все отлично работает.
Теперь, если мы используем ClientLogin с тем же адресом электронной почты и используем HOSTED_OR_GOOGLE для типа учетной записи, вход будет успешным, но он будет использовать размещенную учетную запись, так как размещенная учетная запись имеет приоритет. Как я упоминал выше, вы не можете использовать размещенную учетную запись для приложения, которое ожидает учетную запись Google. Таким образом, аутентификация не будет работать.
Таким образом, при использовании ClientLogin для аутентификации в приложении Google App Engine вам необходимо использовать GOOGLE для типа учетной записи, если приложение предназначено для учетных записей Google, или HOSTED для типа учетной записи, если приложение предназначено для домена.
Спасибо за этот пост и особенно ответ от Кита, но он не работает для меня. Даже если мне кажется, что это нормально... очень странно.
Я проверяю этот пост ( Как вы получаете доступ к аутентифицированному сервису Google App Engine из (не веб) клиента Python?), В котором рассказывается о том же в Python. Я проверяю это, и это работает.
И целевой код C, предложенный Китом, действительно похож на код на python.
Но когда я пытаюсь получить токен "Auth", authData содержит Error=BadAuthentication.
Кто-то получил представление о возможных проблемах?
Обратите внимание, что Google недавно изменил способ обозначения ошибки авторизации. Они использовали для добавления токена ошибки в ответ. Теперь они просто возвращают статус 403 (Запрещено). Это сломало мой код!