19

我设置了一个可可类,我想用它来连接我正在构建的 RESTful Web 服务。我决定像这样在我的 PHP 后端使用 HTTP 基本身份验证……</p>

<?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 正在返回一个错误……</p>

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

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

4

4 回答 4

63

同步NSURLConnection绝对可以与NSURLCredentialStorage. 以下是通常的工作方式:

  1. NSURLConnection从服务器请求页面
  2. 服务器回复 401 响应
  3. NSURLConnection看看它可以从 URL 中收集哪些凭据
  4. 如果 URL 没有提供完整的凭据(用户名和密码),NSURLConnection也会咨询NSURLCredentialStorage以填补空白
  5. 如果仍未确定完整凭据,NSURLConnection将发送-connection:didReceiveAuthenticationChallenge:委托方法请求凭据
  6. 如果NSURLConnectionnow 最终拥有完整的凭据,它会重试包含授权数据的原始请求。

通过使用同步连接方法,您只会失去第 5 步,即提供自定义身份验证的能力。因此,您可以在 URL 中预先提供身份验证凭据,也可以NSURLCredentialStorage在发送请求之前将其放入。例如

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];
于 2009-02-02T00:01:26.100 回答
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(例如它发送而是一个常规页面)。

于 2009-02-03T23:34:17.103 回答
0

我会注意到 mikeabdullahuk 的回答很好,但如果您使用 NSURLCredentialPersistencePermanent 而不是每个会话,它会将凭据存储在用户钥匙串中,因此下次您可以检查 NSURLCredentialStorage 以获取保护空间的默认凭据的非零值,如果您得到一个非零值,您可以只传递凭据。我现在正在为我正在编写的一个美味的.com 客户端使用这种方法,它在我的测试中运行良好。

于 2009-02-02T17:54:18.680 回答
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 将使用此凭据

于 2013-02-13T22:38:51.337 回答