Mar*_*ark 6 objective-c uiwebview nsurlprotocol ios nsurlconnectiondelegate
概观
我正在为一个iOS应用程序进行SAML登录(单点登录,类似于openID)解决方案,该解决方案涉及显示带有a的视图控制器,UIWebView并且在处理HTTP基本/摘要时我遇到了计时和/或超时问题认真的UIWebView.
具体来说,当客户端获得HTTP身份验证质询时,我会弹出UIAlertView提示用户输入用户ID和密码的信息.如果用户能够快速输入信息(<10秒),则可以正常工作.但是,如果条目超过10秒,则连接似乎已终止且没有任何反应.
问题
connection:didReceiveAuthenticationChallenge:会阻止我提示用户输入用户ID和密码(并且必须等待用户输入)?有没有人有解决方法(例如某种方式来延长连接超时)?UIWebView一个子类的HTTP基本/摘要auth NSURLProtocol?细节和代码
对于我们需要处理的大多数SAML系统,登录将显示为常规网页UIWebView.但是,我们需要处理的一些系统可以回退到使用移动浏览器的HTTP基本或HTTP摘要身份验证,因此我们也需要能够处理它.
最大的挑战始于UIWebView不会暴露下面的网络调用.为了得到我需要的东西,我已经创建了一个子类NSURLProtocol并在必要时注册了它:
[NSURLProtocol registerClass:[SMURLProtocol class]];
Run Code Online (Sandbox Code Playgroud)
这样,SMURLProtocol当发出HTTP基本/ auth质询时,会调用此方法,因此我返回YES,我们可以处理HTTP基本和摘要式身份验证:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]
|| [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]);
}
Run Code Online (Sandbox Code Playgroud)
现在我告诉网络堆栈SMURLProtocol可以处理auth挑战,所以它调用
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSString *authenticationMethod = [protectionSpace authenticationMethod];
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]
|| [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
// Stash the challenge in an IVAR so we can use it later
_challenge = challenge;
// These network operations are often on a background thread, so we have to make sure to be on the foreground thread
// to interact with the UI. We tried the UIAlertView performSelectorOnMainThread, but ran into issues, so then
// we switched to GCD with a semaphore?
_dsema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_main_queue(), ^{
// Prompt the user to enter the userID and password
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"AUTHENTICATION_REQUIRED", @"")
message:[protectionSpace host]
delegate:self
cancelButtonTitle:NSLocalizedString(@"CANCEL", @"")
otherButtonTitles:NSLocalizedString(@"LOG_IN", @""), nil];
[alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[alert show];
});
dispatch_semaphore_wait(_dsema, DISPATCH_TIME_FOREVER);
// --> when you get here, the user has responded to the UIAlertView <--
dispatch_release(_dsema);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我正在启动UIAlertView以提示用户输入用户ID和密码.我必须在主线程上做这件事,因为(显然,我不确定)网络代码是在后台线程上运行的.我添加了信号量和显式的Grand Central Dispatch代码来解决我遇到的偶然崩溃(基于这个线程).
最后一部分是UIAlertView委托,它接受用户ID和密码构建挑战的凭证:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (([alertView alertViewStyle] == UIAlertViewStyleLoginAndPasswordInput) && (buttonIndex == 1)) {
NSString *userID = [[alertView textFieldAtIndex:0] text];
NSString *password = [[alertView textFieldAtIndex:1] text];
// when you get the reply that should unblock the background thread, unblock the other thread:
dispatch_semaphore_signal(_dsema);
// Use the userID and password entered by the user to proceed
// with the authentication challenge.
[_challenge.sender useCredential:[NSURLCredential credentialWithUser:userID
password:password
persistence:NSURLCredentialPersistenceNone]
forAuthenticationChallenge:_challenge];
[_challenge.sender continueWithoutCredentialForAuthenticationChallenge:_challenge];
_challenge = nil;
}
}
Run Code Online (Sandbox Code Playgroud)
正如我在概述中所说,如果用户能够在不到10秒的时间内输入用户ID和密码,这一切都很有效.如果需要更长时间,则连接似乎超时,并且将凭据传递给质询的发件人无效.
| 归档时间: |
|
| 查看次数: |
2492 次 |
| 最近记录: |