使用NSURLSession处理复杂认证机制
1. OAuth 2.0认证
- 相关类:
NSURLSession
:用于创建会话,负责管理网络请求。
NSMutableURLRequest
:构建请求对象,可设置请求头、HTTP方法等。
NSURLSessionDataTask
:用于处理数据任务,如发送请求并接收响应。
- 方法调用步骤:
- 获取OAuth 2.0令牌:通常通过向认证服务器发送包含客户端ID、客户端密钥、授权码等信息的请求获取令牌。这可能涉及使用
NSURLSession
发送POST请求。例如:
NSURL *tokenURL = [NSURL URLWithString:@"https://your - auth - server.com/oauth2/token"];
NSMutableURLRequest *tokenRequest = [NSMutableURLRequest requestWithURL:tokenURL];
[tokenRequest setHTTPMethod:@"POST"];
NSString *parameters = @"client_id=your_client_id&client_secret=your_client_secret&grant_type=authorization_code&code=your_code";
[tokenRequest setHTTPBody:[parameters dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask *tokenTask = [[NSURLSession sharedSession] dataTaskWithRequest:tokenRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error && data) {
NSDictionary *tokenResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *accessToken = tokenResponse[@"access_token"];
// 保存accessToken以便后续请求使用
}
}];
[tokenTask resume];
2. **使用令牌进行后续请求**:在构建后续请求时,将令牌添加到请求头中。例如:
NSURL *apiURL = [NSURL URLWithString:@"https://your - api - server.com/api/resource"];
NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL];
[apiRequest setHTTPMethod:@"GET"];
[apiRequest setValue:[NSString stringWithFormat:@"Bearer %@", accessToken] forHTTPHeaderField:@"Authorization"];
NSURLSessionDataTask *apiTask = [[NSURLSession sharedSession] dataTaskWithRequest:apiRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 处理响应
}];
[apiTask resume];
- 可能遇到的问题及解决方案:
- 令牌过期:服务器返回的令牌通常有有效期,当令牌过期时,请求会失败。解决方案是在收到令牌过期的错误响应(如401 Unauthorized)时,重新获取令牌并重新发起请求。
- 认证服务器故障:认证服务器可能出现故障或响应缓慢。可以设置合理的超时时间,并提供适当的错误提示给用户,同时考虑使用重试机制。
2. HTTP Digest认证
- 相关类:
- 同样使用
NSURLSession
、NSMutableURLRequest
和NSURLSessionDataTask
。
- 还需要处理HTTP Digest认证头的相关计算,虽然Objective - C没有内置的专门处理HTTP Digest认证的类,但可以手动计算摘要。
- 方法调用步骤:
- 发起初始请求:发送一个普通的请求到需要认证的服务器资源。
NSURL *digestURL = [NSURL URLWithString:@"https://your - digest - protected - server.com/resource"];
NSMutableURLRequest *digestRequest = [NSMutableURLRequest requestWithURL:digestURL];
[digestRequest setHTTPMethod:@"GET"];
NSURLSessionDataTask *initialTask = [[NSURLSession sharedSession] dataTaskWithRequest:digestRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error && [error code] == NSURLErrorUserAuthenticationRequired) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSString *wwwAuthenticateHeader = [httpResponse allHeaderFields][@"WWW - Authenticate"];
// 解析wwwAuthenticateHeader获取realm、nonce等信息
// 计算digest认证头
NSString *digestHeader = [self calculateDigestHeaderForRequest:digestRequest withWWWAuthenticateHeader:wwwAuthenticateHeader];
[digestRequest setValue:digestHeader forHTTPHeaderField:@"Authorization"];
// 重新发起请求
NSURLSessionDataTask *retryTask = [[NSURLSession sharedSession] dataTaskWithRequest:digestRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 处理响应
}];
[retryTask resume];
}
}];
[initialTask resume];
2. **计算Digest认证头**:计算Digest认证头需要根据从`WWW - Authenticate`头中获取的`realm`、`nonce`等信息,以及用户名、密码和请求方法、URL等。以下是一个简化的计算示例(实际应用中可能需要更复杂的加密计算):
- (NSString *)calculateDigestHeaderForRequest:(NSMutableURLRequest *)request withWWWAuthenticateHeader:(NSString *)wwwAuthenticateHeader {
// 解析wwwAuthenticateHeader获取realm、nonce等
NSDictionary *authInfo = [self parseWWWAuthenticateHeader:wwwAuthenticateHeader];
NSString *realm = authInfo[@"realm"];
NSString *nonce = authInfo[@"nonce"];
NSString *username = @"your_username";
NSString *password = @"your_password";
NSString *method = request.HTTPMethod;
NSString *uri = request.URL.absoluteString;
NSString *ha1 = [self calculateHA1:username password:password realm:realm];
NSString *ha2 = [self calculateHA2:method uri:uri];
NSString *response = [self calculateResponse:ha1 nonce:nonce ha2:ha2];
NSString *digestHeader = [NSString stringWithFormat:@"Digest username=\"%@\", realm=\"%@\", nonce=\"%@\", uri=\"%@\", response=\"%@\"", username, realm, nonce, uri, response];
return digestHeader;
}
- (NSString *)calculateHA1:(NSString *)username password:(NSString *)password realm:(NSString *)realm {
NSString *ha1String = [NSString stringWithFormat:@"%@:%@:%@", username, realm, password];
NSData *ha1Data = [ha1String dataUsingEncoding:NSUTF8StringEncoding];
// 实际应使用合适的哈希算法,如MD5
// 这里简单示例,假设已有计算MD5的方法
NSString *ha1 = [self md5:ha1Data];
return ha1;
}
- (NSString *)calculateHA2:(NSString *)method uri:(NSString *)uri {
NSString *ha2String = [NSString stringWithFormat:@"%@:%@", method, uri];
NSData *ha2Data = [ha2String dataUsingEncoding:NSUTF8StringEncoding];
// 同样假设已有计算MD5的方法
NSString *ha2 = [self md5:ha2Data];
return ha2;
}
- (NSString *)calculateResponse:(NSString *)ha1 nonce:(NSString *)nonce ha2:(NSString *)ha2 {
NSString *responseString = [NSString stringWithFormat:@"%@:%@:%@", ha1, nonce, ha2];
NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
// 假设已有计算MD5的方法
NSString *response = [self md5:responseData];
return response;
}
- (NSString *)md5:(NSData *)data {
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5(data.bytes, (CC_LONG)data.length, digest);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[output appendFormat:@"%02x", digest[i]];
}
return output;
}
- (NSDictionary *)parseWWWAuthenticateHeader:(NSString *)header {
NSMutableDictionary *authInfo = [NSMutableDictionary dictionary];
NSArray *components = [header componentsSeparatedByString:@", "];
for (NSString *component in components) {
NSArray *keyValue = [component componentsSeparatedByString:@"="];
if (keyValue.count == 2) {
NSString *key = keyValue[0];
NSString *value = [keyValue[1] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]];
authInfo[key] = value;
}
}
return authInfo;
}
- 可能遇到的问题及解决方案:
- 摘要计算错误:Digest认证的摘要计算较为复杂,容易出错。仔细检查计算过程,确保使用正确的哈希算法和参数。
- 服务器配置不一致:不同服务器对Digest认证的配置可能略有不同,如对
nonce
的处理等。遇到问题时,仔细检查服务器文档和配置,与服务器管理员沟通。