iOS:使用UIWebView进行HTTP Basic / Digest Auth

时间:2013-01-11 18:59:57

标签: ios objective-c uiwebview nsurlconnectiondelegate nsurlprotocol

概述

我正在为一个iOS应用程序进行SAML登录(单点登录,类似于openID)解决方案,该解决方案涉及显示一个UIWebView的视图控制器,并且我遇到了时间和/或在UIWebView中处理HTTP basic / digest auth时出现超时问题。

具体来说,当客户端获得HTTP身份验证质询时,我弹出UIAlertView提示用户输入用户ID&amp;密码。如果用户能够快速输入信息(<10秒),则可以正常工作。但是,如果条目超过10秒,则连接似乎已终止且没有任何反应。

问题

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

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

    最大的挑战始于UIWebView不会暴露下面的网络调用。为了得到我需要的东西,我创建了一个NSURLProtocol的子类并根据需要进行了注册:

    [NSURLProtocol registerClass:[SMURLProtocol class]];
    

    这样,SMURLProtocol上的这个方法在发出HTTP基本/ auth质询时被调用,所以我返回YES我们可以处理HTTP basic&amp;摘要认证:

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

    现在我告诉网络堆栈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);
    
    }
    }
    

    如您所见,我正在启动UIAlertView以提示用户输入用户ID和密码。我必须在主线程上做这件事,因为(显然,我不确定)网络代码是在后台线程上运行的。我添加了信号量和显式的Grand Central Dispatch代码,以解决我看到的偶然崩溃问题(基于此thread)。

    最后一块是UIAlertView委托,它接受用户ID&amp; password为挑战建立凭证:

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

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

0 个答案:

没有答案