iOS中的NSURLConnection和基本HTTP身份验证

时间:2022-05-25 20:12:36

I need to invoke an initial GET HTTP request with Basic Authentication. This would be the first time the request is sent to the server and I already have the username & password so there's no need for a challenge from the server for authorization.

我需要使用基本的身份验证来调用初始的GET HTTP请求。这将是第一次将请求发送到服务器,并且我已经有了用户名和密码,因此不需要来自服务器的请求进行授权。

First question:

第一个问题:

  1. Does NSURLConnection have to be set as synchronous to do Basic Auth? According to the answer on this post, it seems that you can't do Basic Auth if you opt for the async route.

    NSURLConnection必须设置为同步来执行基本的身份验证吗?根据这篇文章的回答,如果您选择异步路由,似乎您不能执行基本的Auth。

  2. Anyone know of any some sample code that illustrates Basic Auth on a GET request without the need for a challenge response? Apple's documentation shows an example but only after the server has issued the challenge request to the client.

    有谁知道一些示例代码,它演示了GET请求上的基本Auth,而不需要挑战响应?苹果的文档显示了一个示例,但只有在服务器向客户端发出挑战请求之后。

I'm kind of new the networking portion of the SDK and I'm not sure which of the other classes I should use to get this working. (I see the NSURLCredential class but it seems that it is used only with NSURLAuthenticationChallenge after the client has requested for an authorized resource from the server).

我是SDK的联网部分,我不确定我应该用哪个类来实现这个。(我看到了NSURLCredential类,但似乎只有在客户机向服务器请求了授权资源之后,才将它用于NSURLAuthenticationChallenge)。

7 个解决方案

#1


129  

I'm using an asynchronous connection with MGTwitterEngine and it sets the authorization in the NSMutableURLRequest (theRequest) like so:

我正在使用MGTwitterEngine的异步连接,它将NSMutableURLRequest (theRequest)中的授权设置为:

NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];
[theRequest setValue:authValue forHTTPHeaderField:@"Authorization"];

I don't believe this method requires going through the challenge loop but I could be wrong

我不认为这个方法需要经过挑战循环,但我可能是错的

#2


79  

Even the question is answered, I want to present the solution, which doesn't require external libs, I found in another thread:

即使问题得到了回答,我也想给出一个不需要外部lib的解决方案,我在另一个线程中发现:

// Setup NSURLConnection
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL
                                         cachePolicy:NSURLRequestUseProtocolCachePolicy
                                     timeoutInterval:30.0];

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
[connection release];

// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge previousFailureCount] == 0) {
        NSLog(@"received authentication challenge");
        NSURLCredential *newCredential = [NSURLCredential credentialWithUser:@"USER"
                                                                    password:@"PASSWORD"
                                                                 persistence:NSURLCredentialPersistenceForSession];
        NSLog(@"credential created");
        [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
        NSLog(@"responded to authentication challenge");    
    }
    else {
        NSLog(@"previous authentication failure");
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    ...
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    ...
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    ...
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    ...
}

#3


11  

Here is a detailed answer with no 3rd party involved:

以下是没有第三方参与的详细答案:

Please check here:

请检查:

//username and password value
NSString *username = @“your_username”;
NSString *password = @“your_password”;

//HTTP Basic Authentication
NSString *authenticationString = [NSString stringWithFormat:@"%@:%@", username, password]];
NSData *authenticationData = [authenticationString dataUsingEncoding:NSASCIIStringEncoding];
NSString *authenticationValue = [authenticationData base64Encoding];

//Set up your request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.your-api.com/“]];

// Set your user login credentials
[request setValue:[NSString stringWithFormat:@"Basic %@", authenticationValue] forHTTPHeaderField:@"Authorization"];

// Send your request asynchronously
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) {
      if ([responseData length] > 0 && responseError == nil){
            //logic here
      }else if ([responseData length] == 0 && responseError == nil){
             NSLog(@"data error: %@", responseError);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Error accessing the data" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }else if (responseError != nil && responseError.code == NSURLErrorTimedOut){
             NSLog(@"data timeout: %@”, NSURLErrorTimedOut);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"connection timeout" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }else if (responseError != nil){
             NSLog(@"data download error: %@”,responseError);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"data download error" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }
}]

Kindly let me know your feedback on this.

请让我知道你对此的反馈。

Thanks

谢谢

#4


7  

If you don't want to import the whole of MGTwitterEngine and you aren't doing an asynchronous request Then you can use http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch

如果您不想导入整个MGTwitterEngine,并且没有进行异步请求,那么可以使用http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch

To base64 encode the Username and password So replace

要对base64编码用户名和密码进行替换

NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];

with

NSString *encodedLoginData = [Base64 encode:[loginString dataUsingEncoding:NSUTF8StringEncoding]];

after

you will need to include the following file

您需要包含以下文件

static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

@implementation Base64
+(NSString *)encode:(NSData *)plainText {
    int encodedLength = (((([plainText length] % 3) + [plainText length]) / 3) * 4) + 1;
    unsigned char *outputBuffer = malloc(encodedLength);
    unsigned char *inputBuffer = (unsigned char *)[plainText bytes];

    NSInteger i;
    NSInteger j = 0;
    int remain;

    for(i = 0; i < [plainText length]; i += 3) {
        remain = [plainText length] - i;

        outputBuffer[j++] = alphabet[(inputBuffer[i] & 0xFC) >> 2];
        outputBuffer[j++] = alphabet[((inputBuffer[i] & 0x03) << 4) | 
                                     ((remain > 1) ? ((inputBuffer[i + 1] & 0xF0) >> 4): 0)];

        if(remain > 1)
            outputBuffer[j++] = alphabet[((inputBuffer[i + 1] & 0x0F) << 2)
                                         | ((remain > 2) ? ((inputBuffer[i + 2] & 0xC0) >> 6) : 0)];
        else 
            outputBuffer[j++] = '=';

        if(remain > 2)
            outputBuffer[j++] = alphabet[inputBuffer[i + 2] & 0x3F];
        else
            outputBuffer[j++] = '=';            
    }

    outputBuffer[j] = 0;

    NSString *result = [NSString stringWithCString:outputBuffer length:strlen(outputBuffer)];
    free(outputBuffer);

    return result;
}
@end

#5


3  

Since NSData::dataUsingEncoding is deprecated (ios 7.0), you could use this solution:

由于不赞成使用NSData: dataUsingEncoding (ios7.0),您可以使用以下解决方案:

// Forming string with credentials 'myusername:mypassword'
NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password];
// Getting data from it
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
// Encoding data with base64 and converting back to NSString
NSString* authStrData = [[NSString alloc] initWithData:[authData base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithLineFeed] encoding:NSASCIIStringEncoding];
// Forming Basic Authorization string Header
NSString *authValue = [NSString stringWithFormat:@"Basic %@", authStrData];
// Assigning it to request
[request setValue:authValue forHTTPHeaderField:@"Authorization"];

#6


1  

If you are using GTMHTTPFetcher for your connection, basic authentication is fairly easy as well. You simply need to provide the credential to the fetcher before beginning the fetch.

如果您正在为您的连接使用GTMHTTPFetcher,那么基本的身份验证也相当容易。您只需要在开始获取之前向获取程序提供凭据。

NSString * urlString = @"http://www.testurl.com/";
NSURL * url = [NSURL URLWithString:urlString];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];

NSURLCredential * credential = [NSURLCredential credentialWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceForSession];

GTMHTTPFetcher * gFetcher = [GTMHTTPFetcher fetcherWithRequest:request];
gFetcher.credential = credential;

[gFetcher beginFetchWithDelegate:self didFinishSelector:@selector(fetchCompleted:withData:andError:)];

#7


0  

Can you tell me what's the reason behind limiting the encoding line length to 80 in your example code? I thought that HTTP headers have a max length of something like 4k (or maybe some servers don't take anything longer than that). – Justin Galzic Dec 29 '09 at 17:29

您能告诉我在示例代码中将编码行长度限制为80的原因吗?我认为HTTP报头的最大长度是4k(或者有些服务器不会比4k长)。- Justin Galzic 12月29日17:29。

It is not limiting to 80, it is an option of the method base64EncodingWithLineLength in NSData+Base64.h/m, where you can split your encoded string into multiple lines, which is useful for other application, such as nntp transmission. I believe 80 is chosen by the twitter engine author to be a length big enough to accommodate most user/password encoded result to one line.

它不限制为80,它是NSData+Base64中base64EncodingWithLineLength方法的一个选项。h/m,您可以将编码的字符串分割成多行,这对于其他应用程序很有用,比如nntp传输。我认为,twitter引擎作者选择80的长度足以容纳大多数用户/密码编码的结果到一行。

#1


129  

I'm using an asynchronous connection with MGTwitterEngine and it sets the authorization in the NSMutableURLRequest (theRequest) like so:

我正在使用MGTwitterEngine的异步连接,它将NSMutableURLRequest (theRequest)中的授权设置为:

NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];
[theRequest setValue:authValue forHTTPHeaderField:@"Authorization"];

I don't believe this method requires going through the challenge loop but I could be wrong

我不认为这个方法需要经过挑战循环,但我可能是错的

#2


79  

Even the question is answered, I want to present the solution, which doesn't require external libs, I found in another thread:

即使问题得到了回答,我也想给出一个不需要外部lib的解决方案,我在另一个线程中发现:

// Setup NSURLConnection
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL
                                         cachePolicy:NSURLRequestUseProtocolCachePolicy
                                     timeoutInterval:30.0];

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
[connection release];

// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge previousFailureCount] == 0) {
        NSLog(@"received authentication challenge");
        NSURLCredential *newCredential = [NSURLCredential credentialWithUser:@"USER"
                                                                    password:@"PASSWORD"
                                                                 persistence:NSURLCredentialPersistenceForSession];
        NSLog(@"credential created");
        [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
        NSLog(@"responded to authentication challenge");    
    }
    else {
        NSLog(@"previous authentication failure");
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    ...
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    ...
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    ...
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    ...
}

#3


11  

Here is a detailed answer with no 3rd party involved:

以下是没有第三方参与的详细答案:

Please check here:

请检查:

//username and password value
NSString *username = @“your_username”;
NSString *password = @“your_password”;

//HTTP Basic Authentication
NSString *authenticationString = [NSString stringWithFormat:@"%@:%@", username, password]];
NSData *authenticationData = [authenticationString dataUsingEncoding:NSASCIIStringEncoding];
NSString *authenticationValue = [authenticationData base64Encoding];

//Set up your request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.your-api.com/“]];

// Set your user login credentials
[request setValue:[NSString stringWithFormat:@"Basic %@", authenticationValue] forHTTPHeaderField:@"Authorization"];

// Send your request asynchronously
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) {
      if ([responseData length] > 0 && responseError == nil){
            //logic here
      }else if ([responseData length] == 0 && responseError == nil){
             NSLog(@"data error: %@", responseError);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Error accessing the data" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }else if (responseError != nil && responseError.code == NSURLErrorTimedOut){
             NSLog(@"data timeout: %@”, NSURLErrorTimedOut);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"connection timeout" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }else if (responseError != nil){
             NSLog(@"data download error: %@”,responseError);
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"data download error" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
             [alert show];
             [alert release];
      }
}]

Kindly let me know your feedback on this.

请让我知道你对此的反馈。

Thanks

谢谢

#4


7  

If you don't want to import the whole of MGTwitterEngine and you aren't doing an asynchronous request Then you can use http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch

如果您不想导入整个MGTwitterEngine,并且没有进行异步请求,那么可以使用http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch

To base64 encode the Username and password So replace

要对base64编码用户名和密码进行替换

NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];

with

NSString *encodedLoginData = [Base64 encode:[loginString dataUsingEncoding:NSUTF8StringEncoding]];

after

you will need to include the following file

您需要包含以下文件

static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

@implementation Base64
+(NSString *)encode:(NSData *)plainText {
    int encodedLength = (((([plainText length] % 3) + [plainText length]) / 3) * 4) + 1;
    unsigned char *outputBuffer = malloc(encodedLength);
    unsigned char *inputBuffer = (unsigned char *)[plainText bytes];

    NSInteger i;
    NSInteger j = 0;
    int remain;

    for(i = 0; i < [plainText length]; i += 3) {
        remain = [plainText length] - i;

        outputBuffer[j++] = alphabet[(inputBuffer[i] & 0xFC) >> 2];
        outputBuffer[j++] = alphabet[((inputBuffer[i] & 0x03) << 4) | 
                                     ((remain > 1) ? ((inputBuffer[i + 1] & 0xF0) >> 4): 0)];

        if(remain > 1)
            outputBuffer[j++] = alphabet[((inputBuffer[i + 1] & 0x0F) << 2)
                                         | ((remain > 2) ? ((inputBuffer[i + 2] & 0xC0) >> 6) : 0)];
        else 
            outputBuffer[j++] = '=';

        if(remain > 2)
            outputBuffer[j++] = alphabet[inputBuffer[i + 2] & 0x3F];
        else
            outputBuffer[j++] = '=';            
    }

    outputBuffer[j] = 0;

    NSString *result = [NSString stringWithCString:outputBuffer length:strlen(outputBuffer)];
    free(outputBuffer);

    return result;
}
@end

#5


3  

Since NSData::dataUsingEncoding is deprecated (ios 7.0), you could use this solution:

由于不赞成使用NSData: dataUsingEncoding (ios7.0),您可以使用以下解决方案:

// Forming string with credentials 'myusername:mypassword'
NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password];
// Getting data from it
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
// Encoding data with base64 and converting back to NSString
NSString* authStrData = [[NSString alloc] initWithData:[authData base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithLineFeed] encoding:NSASCIIStringEncoding];
// Forming Basic Authorization string Header
NSString *authValue = [NSString stringWithFormat:@"Basic %@", authStrData];
// Assigning it to request
[request setValue:authValue forHTTPHeaderField:@"Authorization"];

#6


1  

If you are using GTMHTTPFetcher for your connection, basic authentication is fairly easy as well. You simply need to provide the credential to the fetcher before beginning the fetch.

如果您正在为您的连接使用GTMHTTPFetcher,那么基本的身份验证也相当容易。您只需要在开始获取之前向获取程序提供凭据。

NSString * urlString = @"http://www.testurl.com/";
NSURL * url = [NSURL URLWithString:urlString];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];

NSURLCredential * credential = [NSURLCredential credentialWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceForSession];

GTMHTTPFetcher * gFetcher = [GTMHTTPFetcher fetcherWithRequest:request];
gFetcher.credential = credential;

[gFetcher beginFetchWithDelegate:self didFinishSelector:@selector(fetchCompleted:withData:andError:)];

#7


0  

Can you tell me what's the reason behind limiting the encoding line length to 80 in your example code? I thought that HTTP headers have a max length of something like 4k (or maybe some servers don't take anything longer than that). – Justin Galzic Dec 29 '09 at 17:29

您能告诉我在示例代码中将编码行长度限制为80的原因吗?我认为HTTP报头的最大长度是4k(或者有些服务器不会比4k长)。- Justin Galzic 12月29日17:29。

It is not limiting to 80, it is an option of the method base64EncodingWithLineLength in NSData+Base64.h/m, where you can split your encoded string into multiple lines, which is useful for other application, such as nntp transmission. I believe 80 is chosen by the twitter engine author to be a length big enough to accommodate most user/password encoded result to one line.

它不限制为80,它是NSData+Base64中base64EncodingWithLineLength方法的一个选项。h/m,您可以将编码的字符串分割成多行,这对于其他应用程序很有用,比如nntp传输。我认为,twitter引擎作者选择80的长度足以容纳大多数用户/密码编码的结果到一行。