ios - NSURLConnection 和 NSMutableURLRequest 认证

标签 ios objective-c http nsurl

我有一个 iOS 应用程序,它将向网站提交用户名和密码,并检索按“登录”后加载的网页的 HTML 代码:https://grades.bsd405.org/Pinnacle/Gradebook/Logon.aspx

表单数据字段如下:

__LASTFOCUS、__EVENTTARGET、__EVENTARGUMENT、__VIEWSTATE、__EVENTVALIDATION、ctl00$ContentPlaceHolder$用户名、ctl00$ContentPlaceHolder$密码、ctl00$ContentPlaceHolder$lstDomains、ctl00$ContentPlaceHolder$LogonButton、PageUniqueId

这是我目前的代码:

NSURL * url = [NSURL URLWithString:@"https://grades.bsd405.org/Pinnacle/Gradebook/Logon.aspx?ReturnUrl=%2fPinnacle%2fGradebook%2fDefault.aspx "];

NSString *post = @"__LASTFOCUS&__EVENTTARGET&__EVENTARGUMENT&__VIEWSTATE=/wEPDwUJNTkxNzI3MDIzD2QWAmYPZBYCAgMPZBYGAgEPZBYCAgkPZBYCAgEPZBYIAgMPFgIeB1Zpc2libGVoZAIFDxYCHwBoZAIHDxYCHwBoZAIJDxYCHgVzdHlsZQUjdmVydGljYWwtYWxpZ246bWlkZGxlO2Rpc3BsYXk6bm9uZTtkAgMPDxYCHwBoZGQCBQ9kFghmD2QWAgINDxYCHgVjbGFzcwUQc2luZ2xlU2Nob29sTGlzdBYCAgEPZBYCAgEPEGQPFgFmFgEQBQ5EZWZhdWx0IERvbWFpbgUIUGlubmFjbGVnZGQCAg9kFgICEw9kFgICAQ9kFgICAQ8QZGQWAGQCBw8PFgIeBFRleHQFIFBpbm5hY2xlIEdyYWRlIDIwMTIgV2ludGVyIEJyZWFrZGQCCA8PFgIfAwU3Q29weXJpZ2h0IChjKSAyMDEzIEdsb2JhbFNjaG9sYXIuICBBbGwgcmlnaHRzIHJlc2VydmVkLmRkZP/l6irI9peZfyqpKjk3fwLuEbos&__EVENTVALIDATION=/wEWBgKjnbqUCQLnksmgAQKTpbWbDgLB5+KIBAL4xb20BAK20ZqiCel6sQLBsF1W3XHOxpgq+tJj+Rx2&ctl00$ContentPlaceHolder$Username=TESTUSERNAME&ctl00$ContentPlaceHolder$Password=TESTPASSWORD&ctl00$ContentPlaceHolder$lstDomains=Pinnacle&ctl00$ContentPlaceHolder$LogonButton=Signin&PageUniqueId=2dacba26-bb0d-412f-b06a-e02caf039c4b";

NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

[self clearCookiesForURL:url];
[request setURL:url];

[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];

NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];

但是,总是会返回一个错误页面。我究竟做错了什么? 我还实现了以下内容:

-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"HEREEE");
if ([challenge previousFailureCount] == 0) {
    NSLog(@"received authentication challenge");
    NSURLCredential *newCredential = [NSURLCredential credentialWithUser:@"USERNAME"
                                                                password:@"PASSWORD"
                                                             persistence:NSURLCredentialPersistenceForSession];
    NSLog(@"credential created");
    [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
    NSLog(@"responded to authentication challenge");
}
else {
    NSLog(@"previous authentication failure");
}

但是这个方法永远不会被调用!

我的 View Controller 符合:
NSURLConnectionDelegate,NSURLAuthenticationChallengeSender,NSURLConnectionDataDelegate

谢谢!

最佳答案

几个想法:

  1. 令我惊讶的是 __VIEWSTATE__EVENTVALIDATION 中存在保留字符。每the x-www-form-urlencoded spec , 你应该是 *, -, ., 0-9< 以外的转义字符百分比A-Z_a-z

    POST 值的标准百分比转义例程是:

    - (NSString *)percentEscapeString:(NSString *)string
    { 
        NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                     (CFStringRef)string,
                                                                                     (CFStringRef)@" ",
                                                                                     (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                     kCFStringEncodingUTF8));
        return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    }
    
  2. 我不确定它是否重要,但您缺少 = 个字符。所以,我希望 __LASTFOCUS=&... 而不是 __LASTFOCUS&...

    将这两点放在一起,生成的有效负载应该类似于:

    __LASTFOCUS=&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwUJNTkxNzI3MDIzD2QWAmYPZBYCAgMPZBYGAgEPZBYCAgkPZBYCAgEPZBYIAgMPFgIeB1Zpc2libGVoZAIFDxYCHwBoZAIHDxYCHwBoZAIJDxYCHgVzdHlsZQUjdmVydGljYWwtYWxpZ246bWlkZGxlO2Rpc3BsYXk6bm9uZTtkAgMPDxYCHwBoZGQCBQ9kFghmD2QWAgINDxYCHgVjbGFzcwUQc2luZ2xlU2Nob29sTGlzdBYCAgEPZBYCAgEPEGQPFgFmFgEQBQ5EZWZhdWx0IERvbWFpbgUIUGlubmFjbGVnZGQCAg9kFgICEw9kFgICAQ9kFgICAQ8QZGQWAGQCBw8PFgIeBFRleHQFIFBpbm5hY2xlIEdyYWRlIDIwMTIgV2ludGVyIEJyZWFrZGQCCA8PFgIfAwU3Q29weXJpZ2h0IChjKSAyMDEzIEdsb2JhbFNjaG9sYXIuICBBbGwgcmlnaHRzIHJlc2VydmVkLmRkZP%2Fl6irI9peZfyqpKjk3fwLuEbos&__EVENTVALIDATION=%2FwEWBgKjnbqUCQLnksmgAQKTpbWbDgLB5%2BKIBAL4xb20BAK20ZqiCel6sQLBsF1W3XHOxpgq%2BtJj%2BRx2&ctl00%24ContentPlaceHolder%24Username=a&ctl00%24ContentPlaceHolder%24Password=b&ctl00%24ContentPlaceHolder%24lstDomains=Pinnacle&ctl00%24ContentPlaceHolder%24LogonButton=Sign+in&PageUniqueId=a3113b01-cf2f-4081-8a1a-d8557e7347de
    

    注意整个请求正文中出现的百分比转义。我通过查看 Charles 中的请求确定了正文.

  3. 我很惊讶地看到 __VIEWSTATE__EVENTVALIDATIONPageUniqueId 像这样硬编码。我可以很容易地想象为这些变量使用固定值会有问题。这完全取决于网站的编程方式。我了解到您正在尝试以编程方式访问不是 Web 服务而是 HTML 界面的网站,这可能会出现问题。

    在尝试登录之前,您可能必须先使用 Logins.aspx 执行 GET 请求,然后从初始登录 HTML 解析这些值。当尝试以编程方式与 Web 服务器交互时,您必须小心调用 Web 浏览器会调用的所有页面,因为您的应用也应该使用像这样的服务器状态变量。

  4. 不相关,但您不需要指定 Content-length,因为 NSURLConnection 会根据您的 的大小为您提供>http正文

  5. 如果您要使用 didReceiveAuthenticationChallenge,请注意您应确保 else 子句调用 cancelAuthenticationChallengecontinueWithoutCredentialForAuthenticationChallenge。您必须始终调用这三种挑战方法中的一种。

    话虽如此,尚不清楚 didReceiveAuthenticationChallenge 是否会被调用。您的服务器可能正在使用一些非基于质询的服务器应用程序级身份验证。

归根结底,我们很难根据目前提供的内容来诊断问题的根源。我会联系 HTML 界面的作者并询问身份验证过程的细节。

关于ios - NSURLConnection 和 NSMutableURLRequest 认证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23398368/

相关文章:

http - <audio>标签上的基本http身份验证?

ios - UITextView 不更新它的 contentSize

iphone - 访问无法修改的父类扩展中定义的 Obj-C 属性

objective-c - 使用 RestKit/iOS 映射嵌套数组

ios - 为什么苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?

android - Android 是否支持同时多个 HTTP 请求?

iphone - 如何控制 UISlider Value Changed-events 频率?

objective-c - 在 Objective C 中使用未声明的标识符

ios - 在特定时间和日期调用 iOS 应用程序

javascript - 什么会导致浏览器 (Chrome) 生成下载