web-dev-qa-db-ja.com

HTTP基本認証にNSURLCredentialStorageを使用できますか?

構築しているRESTfulWebサービスに接続するために使用するcocoaクラスを設定しています。 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ドキュメントの状態では認証のサポートが少ないことを理解しています。

しかし、それはまったく可能ですか? NSURLProtectionSpaces、NSURLCredentials、または任意の認証クラスを使用せずに、Cookie認証を非常に簡単に行うことができます。また、Cocoa認証クラスについてもっと読むことができるリソースはありますか?

ありがとう。

更新:mikeabdullahukあなたが提供したコード(2番目の例)は、私が書いたものとほとんど同じです。さらに調査を行ったところ、NSURLConnectionがエラーを返していることがわかりました…

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

コードはNSURLErrorUserCancelledAuthenticationに対応しています。したがって、私のコードはNSURLCredentialStorageにアクセスしておらず、代わりに認証をキャンセルしているようです。これはPHP HTTP認証関数と関係がありますか?この時点でかなり混乱しています。

19
mazniak

同期NSURLConnectionNSURLCredentialStorageで絶対に機能します。通常の動作は次のとおりです。

  1. NSURLConnectionはサーバーにページを要求します
  2. サーバーは401応答で応答します
  3. NSURLConnectionは、URLから収集できる資格情報を確認します
  4. URLがnot完全な資格情報(ユーザー名とパスワード)を提供した場合、NSURLConnectionNSURLCredentialStorageも参照してギャップを埋めます
  5. 完全な資格情報がまだ決定されていない場合、NSURLConnectionは資格情報を要求する-connection:didReceiveAuthenticationChallenge:デリゲートメソッドを送信します
  6. NSURLConnectionが最終的に完全な認証情報を取得した場合、認証データを含む元のリクエストを再試行します。

同期接続方式を使用すると、のみステップ5で、カスタム認証を提供する機能が失われます。したがって、URLで認証資格情報を事前に提供するか、リクエストを送信する前にそれらをNSURLCredentialStorageに配置することができます。例えば.

NSURLRequest *request =
  [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:[email protected]"]];

[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];
62
Mike Abdullah

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クリーンではなく、サーバーが実際にHTTP 401を送信していないためにNSURLRequestがNSURLCredentialStorageの参照を拒否する状況には耐性があります(たとえば、代わりに通常のページ)。

12
Matt Gallagher

Mikeabdullahukの答えは良いと思いますが、セッションごとではなくNSURLCredentialPersistencePermanentを使用すると、ユーザーのキーチェーンに資格情報が保存されるため、次回、NSURLCredentialStorageで保護スペースのデフォルトの資格情報のnil以外の値を確認できます。 nil以外の値を指定すると、資格情報を渡すことができます。現在、このメソッドを、作成中のdelicious.comクライアントに使用しており、テストで非常にうまく機能します。

0
Colin Wheeler

資格情報を保護スペースのデフォルトの資格情報として設定します。

// 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は、この資格情報を使用します

0
quellish