6

概述

我正在为 iOS 应用程序开发 SAML 登录(单点登录,类似于 openID)解决方案,该解决方案涉及显示带有 a 的视图控制器,UIWebView并且在处理 HTTP 基本/摘要时遇到了计时和/或超时问题中的身份验证UIWebView

具体来说,当客户端收到 HTTP 身份验证质询时,我会弹出一个UIAlertView提示用户输入用户 ID 和密码的提示。如果用户能够快速输入信息(< 10 秒),它就可以工作。但是,如果该条目花费的时间超过 10 秒,则连接似乎已终止,并且没有任何反应。

问题

  1. 调用是否有超时connection:didReceiveAuthenticationChallenge:会阻止我提示用户输入用户 ID 和密码(并且必须等待用户输入)?有没有人有解决方法(例如,某种方式来延长连接超时)?
  2. UIWebView有没有比 的子类更好的方法来处理来自 a 的 HTTP 基本/摘要身份验证NSURLProtocol

详细信息和代码

对于我们需要处理的大多数 SAML 系统,登录将在UIWebView. 但是,我们需要处理的一些系统回退到对移动浏览器使用 HTTP 基本或 HTTP 摘要身份验证,因此我们也需要能够处理它。

最大的挑战始于 UIWebView 不暴露下面的网络调用这一事实。为了满足我的需要,我创建了一个子类NSURLProtocol并根据需要注册了它:

[NSURLProtocol registerClass:[SMURLProtocol class]];

SMURLProtocol这样,当发出 HTTP 基本/身份验证质询时,就会调用此方法,所以我返回 YES,我们可以处理 HTTP 基本和摘要身份验证:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]
        || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]);
}

现在我已经告诉网络堆栈 SMURLProtocol 可以处理身份验证挑战,所以它调用

- (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);

}
}

如您所见,我正在启动一个 UIAlertView 来提示用户输入用户 ID 和密码。我必须在主线程上执行此操作,因为(显然,我不确定)网络代码在后台线程上运行。我添加了信号量和明确的 Grand Central Dispatch 代码来解决我看到的偶尔崩溃(基于此线程)。

最后一部分是接受用户 ID 和密码的 UIAlertView 委托为挑战构建凭证:

- (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;
}
}

正如我在概述中所说,如果用户能够在不到 10 秒的时间内输入用户 ID 和密码,这一切都很好。如果需要更长的时间,则连接似乎会超时,并且将凭据传递给质询的发件人无效。

4

0 回答 0