0

这是我编写的用于连接到服务器并获取用户身份验证令牌的方法:

+ (void)getAuthTokenForUsername:(NSString *)username
                       password:(NSString *)password
              completionHandler:(void (^)(NSString *, NSError *))completionHandler
{
    username = [username URLEncodedString];
    password = [password URLEncodedString];

    NSString *format = @"https://%@:%@@api.example.com/v1/user/api_token?format=json";
    NSString *string = [NSString stringWithFormat:format, username, password];
    NSURL *URL = [NSURL URLWithString:string];
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:URL]
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *URLResponse, NSData *data, NSError *error)
    {
        NSString *token;
        if (data) {
            NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
            token = [NSString stringWithFormat:@"%@:%@", username, dictionary[@"result"]];
        }
        completionHandler(token, error);
    }];
}

然后一个 URL 看起来像这样:https://username:hello%C2%B0@api.example.com/v1/user/api_token?format\=json,其中密码是hello°。该URLEncodedString方法正确地编码了示例中的所有内容,但请求永远不会起作用。问题不在于转义或服务器,因为我可以curl使用相同的 URL,并且我得到了很好的 JSON 和身份验证工作,即使密码中有一个非 ASCII 字符。它也适用于其他编程语言,如rubyor python。但是相同的 url 永远无法使用,NSURLConnection而且它在 Safari 中也无法使用,当然它使用NSURLConnection. 我每次都收到“无法完成操作”和“401 禁止”。

(当密码只包含 ASCII 字符时,我的代码可以正常工作。我也尝试使用这些NSURLCredential方法,同样的问题。)

我需要做什么才能使用密码包含非 ASCII 字符NSURLConnection的 URL ?https://username:hello%C2%B0@api.example.com/v1/user/api_token?format\=json

4

1 回答 1

1

我刚刚对我的模型服务器进行了几次测试,我想我有一个适合你的解决方案。

首先,当您将用户名和密码添加到 URL 时,它们实际上并没有作为 URL 的一部分发送到服务器。它们作为Authorization标头的一部分发送(请参阅基本访问身份验证)。

对您来说最快的解决方法是

NSURLRequest* request = [NSURLRequest requestWithURL:URL];
NSString* usernamePassword = [[NSString stringWithFormat:@"%@:%@", username, password] base64Encode];
[request setValue:[NSString stringWithFormat:@"Basic %@", usernamePassword] forHTTPHeaderField:@"Authorization"] 

为了理解这个问题,让我们再深入一点。让我们忘记NSURLConnection sendAsynchronousRequest:,让我们用NSURLConnectionDelegate. 然后在委托中,让我们定义以下方法:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    return YES;
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    NSLog(@"Proposal: %@ - %@", challenge.proposedCredential.user, challenge.proposedCredential.password);

    NSURLCredential* credential = [NSURLCredential credentialWithUser:@"username"    
                                                             password:@"hello°"
                                                          persistence:NSURLCredentialPersistenceNone];

    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}

如果您不创建这些方法,则您的 URL 中的用户名和密码将永远不会添加到 HTTP 标头中。如果添加它们,您会看到建议的密码是hello%C2%B0. 显然,这是错误的。

您可以直接从

NSLog(@"Password: %@", [[NSURL URLWithString:@"http://username:hello%C2%B0@www.google.com"] password]);

哪个打印

hello%C2%B0  

我相信这是框架中的一个错误NSURL返回password编码并且NSURLCredential不解码它所以我们留下了无效的密码。

于 2013-05-10T12:50:26.367 回答