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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1973325/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:43:56  来源:igfitidea点击:

NSURLConnection and Basic HTTP Authentication in iOS

iosiphoneobjective-cnsurlconnection

提问by Alexi Groove

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

我需要GET HTTP request用 Basic调用一个首字母Authentication。这将是第一次将请求发送到服务器,而我已经拥有了,username & password因此不需要来自服务器的授权挑战。

First question:

第一个问题:

  1. Does NSURLConnectionhave 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.

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

  1. 是否NSURLConnection必须设置为同步才能进行基本身份验证?根据这篇文章的答案,如果您选择异步路由,则似乎无法进行基本身份验证。

  2. 任何人都知道一些示例代码可以GET request在不需要质询响应的情况下说明基本身份验证?Apple 的文档显示了一个示例,但仅在服务器向客户端发出质询请求之后。

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 NSURLCredentialclass but it seems that it is used only with NSURLAuthenticationChallengeafter the client has requested for an authorized resource from the server).

我对 SDK 的网络部分有点陌生,我不确定我应该使用哪些其他类来使其工作。(我看到了NSURLCredential该类,但似乎只有NSURLAuthenticationChallenge在客户端从服务器请求授权资源后才使用它)。

回答by catsby

I'm using an asynchronous connection with MGTwitterEngineand 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

我不相信这种方法需要通过挑战循环,但我可能是错的

回答by dom

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

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

// 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 {
    ...
}

回答by user3045072

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

谢谢

回答by Luke

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

回答by Artem Zaytsev

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

由于 NSData::dataUsingEncoding 已弃用(ios 7.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"];

回答by John M. P. Knox

If you are using GTMHTTPFetcherfor 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:)];

回答by Gaius Parx

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(或者可能有些服务器不需要比这更长的时间)。– 贾斯汀·加尔齐奇 2009 年 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.h/m中base64EncodingWithLineLength方法的一个选项,可以将你的编码字符串拆分成多行,这对于其他应用很有用,比如nntp传输。我相信 80 是 twitter 引擎作者选择的一个足够大的长度,可以将大多数用户/密码编码结果容纳在一行中。

回答by omanosoft

You can use AFNetworking(it is opensource), here is code that worked for me. This code sends file with basic authentication. Just change url, email and password.

您可以使用AFNetworking(它是开源的),这是对我有用的代码。此代码发送具有基本身份验证的文件。只需更改网址,电子邮件和密码即可。

NSString *serverUrl = [NSString stringWithFormat:@"http://www.yoursite.com/uploadlink", profile.host];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:serverUrl parameters:nil error:nil];


NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

// Forming string with credentials 'myusername:mypassword'
NSString *authStr = [NSString stringWithFormat:@"%@:%@", email, emailPassword];
// 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"];

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

NSURL *filePath = [NSURL fileURLWithPath:[url path]];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
     dispatch_async(dispatch_get_main_queue(), ^{
                //Update the progress view
                LLog(@"progres increase... %@ , fraction: %f", uploadProgress.debugDescription, uploadProgress.fractionCompleted);
            });
        } completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
            if (error) {
                NSLog(@"Error: %@", error);
            } else {
                NSLog(@"Success: %@ %@", response, responseObject);
            }
        }];
[uploadTask resume];