objective-c 我可以使用 NSURLCredentialStorage 进行 HTTP 基本身份验证吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/501231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I use NSURLCredentialStorage for HTTP Basic Authentication?
提问by mazniak
I have a cocoa class set up that I want to use to connect to a RESTful web service I'm building. I have decided to use HTTP Basic Authentication on my PHP backend like so…
我设置了一个可可类,我想用它来连接到我正在构建的 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.";
}
?>
At this point I'm using a synchronous NSURLConnection, which I understand the Apple documentation states has less support for Authentication.
在这一点上,我使用的是同步 NSURLConnection,据我所知,Apple 文档声明对身份验证的支持较少。
But is it even possible at all? I can do cookie authentication very easily sans NSURLProtectionSpaces or NSURLCredentials or any of the authentication classes. Also, are there any resources where I can read more about the Cocoa Authentication classes?
但它甚至可能吗?我可以非常轻松地进行 cookie 身份验证,无需 NSURLProtectionSpaces 或 NSURLCredentials 或任何身份验证类。另外,是否有任何资源可以让我阅读有关 Cocoa 身份验证类的更多信息?
Thanks.
谢谢。
UPDATE: mikeabdullahuk The code you supplied (the second example) is almost identical to what I had written. I have done some more investigating, and discovered that the NSURLConnection is returning an error…
更新:mikeabdullahuk 你提供的代码(第二个例子)几乎和我写的一样。我做了一些更多的调查,发现 NSURLConnection 正在返回一个错误......
Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)"
The code corresponds to NSURLErrorUserCancelledAuthentication. So apparently my code is not accessing the NSURLCredentialStorage and instead is canceling the authentication. Could this have anything to do with the PHP HTTP Authentication functions? I'm quite confused at this point.
该代码对应于 NSURLErrorUserCancelledAuthentication。所以显然我的代码没有访问 NSURLCredentialStorage 而是取消身份验证。这可能与 PHP HTTP 身份验证功能有关吗?在这一点上我很困惑。
回答by Mike Abdullah
A synchronous NSURLConnectionwill absolutely work with NSURLCredentialStorage. Here's how things usually work:
同步NSURLConnection绝对适用于NSURLCredentialStorage. 以下是通常的工作方式:
NSURLConnectionrequests the page from the server- The server replies with a 401 response
NSURLConnectionlooks to see what credentials it can glean from the URL- If the URL did notprovide full credentials (username and password),
NSURLConnectionwill also consultNSURLCredentialStorageto fill in the gaps - If full credentials have stillnot been determined,
NSURLConnectionwill send the-connection:didReceiveAuthenticationChallenge:delegate method asking for credentials - If the
NSURLConnectionnow finally has full credentials, it retries the original request including authorization data.
NSURLConnection从服务器请求页面- 服务器回复 401 响应
NSURLConnection查看它可以从 URL 收集哪些凭据- 如果URL并没有提供完整的凭据(用户名和密码),
NSURLConnection亦会咨询NSURLCredentialStorage,填补了国内空白 - 如果仍未确定完整凭据,
NSURLConnection将发送-connection:didReceiveAuthenticationChallenge:委托方法要求凭据 - 如果
NSURLConnection现在最终拥有完整的凭据,它会重试原始请求,包括授权数据。
By using the synchronous connection method, you onlylose out on step 5, the ability to provide custom authentication. So, you can either pre-provide authentication credentials in the URL, or place them in NSURLCredentialStoragebefore sending the request. e.g.
通过使用同步连接方法,您只会失去第 5 步,即提供自定义身份验证的能力。因此,您可以在 URL 中预先提供身份验证凭据,或者NSURLCredentialStorage在发送请求之前将它们放入。例如
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:[email protected]"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
or:
或者:
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];
回答by Matt Gallagher
In a situation where a 401 or other authentication challenge is unacceptable/impossible, I sometimes use a dummy CFHTTPMessage to generate the authetication line, then copy that back into the NSURLRequest:
在 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"];
This may seem completely a bizarre way to do it but it is tolerant of situations where the username/password aren't URL clean and where NSURLRequest refuses to consult the NSURLCredentialStorage because the server isn't actually sending a HTTP 401 (for example it sends a regular page instead).
这可能看起来完全是一种奇怪的方法,但它可以容忍用户名/密码不是 URL 干净的情况以及 NSURLRequest 拒绝咨询 NSURLCredentialStorage 的情况,因为服务器实际上没有发送 HTTP 401(例如它发送改为常规页面)。
回答by quellish
Set your credential as the default credential for the protectionspace:
将您的凭据设置为保护空间的默认凭据:
// 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];
At this point, any subsequent NSURLConnection that is challenged using a protection space that matches what you set will use this credential
此时,使用与您设置的内容匹配的保护空间挑战的任何后续 NSURLConnection 都将使用此凭据
回答by Colin Wheeler
I would note mikeabdullahuk's answer is good but also if you use NSURLCredentialPersistencePermanent instead of per session it will store the credentials in the users keychain so next time you can check NSURLCredentialStorage for a non nil value for the default credentials for a protection space and if you get a non nil value you can just pass the credentials in. I am using this method right now for a delicious.com client I am writing and it works very well in my tests.
我会注意到 mikeabdullahuk 的答案很好,但如果您使用 NSURLCredentialPersistencePermanent 而不是每个会话,它会将凭据存储在用户钥匙串中,因此下次您可以检查 NSURLCredentialStorage 是否为保护空间的默认凭据的非 nil 值,如果您得到一个非 nil 值,你可以只传递凭据。我现在正在为我正在编写的美味.com 客户端使用这种方法,它在我的测试中运行良好。

