我可以使用NSURLCredentialStorage进行HTTP基本身份验证吗?

时间:2009-02-01 17:42:58

标签: objective-c cocoa http authentication https

我有一个cocoa类设置,我想用它连接到我正在构建的RESTful Web服务。我决定在我的PHP后端使用HTTP基本身份验证,就像这样......

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    //Stuff that users will see if they click 'Cancel'
    exit;
}
else {
    //Validation Code
    echo "You entered info.";
}
?>

此时我正在使用同步NSURLConnection,我了解Apple文档状态对身份验证的支持较少。

但它甚至可能吗?我可以非常轻松地进行cookie身份验证,无需NSURLProtectionSpaces或NSURLCredentials或任何身份验证类。另外,有没有我可以阅读更多有关Cocoa身份验证类的资源?

感谢。

更新:mikeabdullahuk 您提供的代码(第二个示例)几乎与我编写的代码相同。我做了一些调查,发现NSURLConnection返回错误......

Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)"

代码对应于NSURLErrorUserCancelledAuthentication。显然我的代码没有访问NSURLCredentialStorage,而是取消了身份验证。这可能与PHP HTTP身份验证功能有关吗?我现在很困惑。

4 个答案:

答案 0 :(得分:62)

同步NSURLConnection绝对适用于NSURLCredentialStorage。以下是通常的工作方式:

  1. NSURLConnection从服务器请求页面
  2. 服务器回复401回复
  3. NSURLConnection希望了解它可以从网址中收集哪些凭据
  4. 如果网址提供完整凭据(用户名和密码),NSURLConnection也会咨询NSURLCredentialStorage以填补空白
  5. 如果完整凭据仍未尚未确定,则NSURLConnection将发送-connection:didReceiveAuthenticationChallenge:委托方法,要求提供凭据
  6. 如果NSURLConnection现在终于拥有完整凭据,则会重试原始请求,包括授权数据。
  7. 通过使用同步连接方法,您在第5步失败,即提供自定义身份验证的功能。因此,您可以在URL中预先提供身份验证凭据,也可以在发送请求之前将其置于NSURLCredentialStorage中。 e.g。

    NSURLRequest *request =
      [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:pass@example.com"]];
    
    [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
    

    或:

    NSURLCredential *credential = [NSURLCredential credentialWithUser:@"user"
                                                             password:@"pass"
                                                          persistence:NSURLCredentialPersistenceForSession];
    
    NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
      initWithHost:@"example.com"
      port:0
      protocol:@"http"
      realm:nil
      authenticationMethod:nil];
    
    
    [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
                                                        forProtectionSpace:protectionSpace];
    [protectionSpace release];
    
    NSURLRequest *request =
      [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]];
    
    [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
    

答案 1 :(得分:12)

在401或其他身份验证挑战不可接受/不可能的情况下,我有时会使用虚拟CFHTTPMessage生成验证行,然后将其复制回NSURLRequest:

// assume NSString *username and *password exist and NSURLRequest *urlRequest
// exists and is fully configured except for HTTP Basic Authentication.. 

CFHTTPMessageRef dummyRequest =
    CFHTTPMessageCreateRequest(
        kCFAllocatorDefault,
        CFSTR("GET"),
        (CFURLRef)[urlRequest URL],
        kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
    dummyRequest,
    nil,
    (CFStringRef)username,
    (CFStringRef)password,
    kCFHTTPAuthenticationSchemeBasic,
    FALSE);
authorizationString =
    (NSString *)CFHTTPMessageCopyHeaderFieldValue(
        dummyRequest,
        CFSTR("Authorization"));
CFRelease(dummyRequest);

[urlRequest setValue:authorizationString forHTTPHeaderField:@"Authorization"];

这可能看起来完全是一种奇怪的方式,但它可以容忍用户名/密码不是URL清理的情况,以及NSURLRequest拒绝咨询NSURLCredentialStorage的情况,因为服务器实际上并没有发送HTTP 401(对于例如,它发送一个常规页面。)

答案 2 :(得分:0)

我会注意到mikeabdullahuk的答案很好,但如果您使用NSURLCredentialPersistencePermanent而不是每个会话,它会将凭据存储在用户钥匙串中,以便下次您可以检查NSURLCredentialStorage是否为保护空间的默认凭据的非零值和如果你得到一个非零值你可以直接传递凭证。我现在正在使用这个方法为我正在写的delicious.com客户端,它在我的测试中运行良好。

答案 3 :(得分:0)

将您的凭据设置为保护空间的默认凭据:

// Permananent, session, whatever.
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent];
// Make sure that if the server you are accessing presents a realm, you set it here.
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"blah.com" port:0 protocol:@"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
// Store it
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];

此时,使用与您设置的保护空间匹配的任何后续NSURLConnection将使用此凭证