iOS Communicating with Authenticating HTTP Servers 与HTTP服务器通信认证 官方文档翻译(六)

原文地址:developer

召集翻译人手:请加入486111671  ios 官方文档翻译

                                                           与HTTP服务器通信认证

本章描述了如何与HTTP服务器身份验证利用CFHTTPAuthentication API。它解释了如何找到匹配验证对象和凭证,将它们应用到一个HTTP请求,并将它们存储供以后使用。

一般来说,如果一个HTTP服务器将返回一个401或407响应后你的HTTP请求,这意味着服务器进行身份验证,需要凭证。在CFHTTPAuthentication API中,每个组凭证存储在一个CFHTTPAuthentication对象。因此,每一个不同的服务器和每个不同的用户连接到服务器进行身份验证需要一个单独的CFHTTPAuthentication对象。与服务器通信,需要CFHTTPAuthentication对象应用于HTTP请求。更详细地解释了这些步骤。

处理身份验证

添加支持身份验证将允许您的应用程序和HTTP服务器进行身份验证(如果服务器返回一个401或407响应)。尽管HTTP身份验证不是一个困难的概念,它是一个复杂的过程来执行。过程如下:

1.客户端向服务器发送一个HTTP请求。

2.服务器给客户端返回一个要求。

3.客户端包凭证的原始请求并将它们发送回服务器。

4.客户端和服务器之间进行协商。

5.当服务器对客户端进行身份验证,它会反回响应请求。

执行这个过程需要许多步骤。图的整个过程如图4 - 1和图4 - 2中可以看到。

图4 - 1处理身份验证

图4 - 2找到一个身份验证对象

当HTTP请求返回一个401或407响应,第一步是为客户找到一个有效的CFHTTPAuthentication对象。身份验证对象包含凭证和其他信息,当应用于HTTP消息请求,与服务器验证你的身份。如果你已经与服务器身份验证一次,你会有一个有效的身份验证对象。然而,在大多数情况下,您将需要创建这个对象与CFHTTPAuthenticationCreateFromResponse响应函数。参见清单4。

注意:所有的示例代码关于身份验证是改编自ImageClient应用程序。

清单4创建身份验证对象

if (!authentication) {

CFHTTPMessageRef responseHeader =

(CFHTTPMessageRef) CFReadStreamCopyProperty(

readStream,

kCFStreamPropertyHTTPResponseHeader

);

// Get the authentication information from the response.

authentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);

CFRelease(responseHeader);

}

如果新的认证对象是有效的,那么你已经完成,并可以继续第二步的图4 - 1。如果身份验证对象不是有效的,然后扔掉身份验证对象和凭证,检查凭证是坏的。关于证书的更多信息,阅读"Security Credentials”。

坏凭证意味着服务器不接受登录信息,它将继续监听新的凭证。然而,如果服务器证书是好的但是仍然拒绝了你的请求,然后服务器拒绝和你说话,所以你必须放弃。假设凭证是坏,重试整个过程开始创建身份验证对象,直到你得到工作凭证和有效的验证对象。在代码中,这个过程应该类似于清单4 - 2。

清单4 - 2找到一个有效的身份验证对象

CFStreamError err;

if (!authentication) {

// the newly created authentication object is bad, must return

return;

} else if (!CFHTTPAuthenticationIsValid(authentication, &err)) {

// destroy authentication and credentials

if (credentials) {

CFRelease(credentials);

credentials = NULL;

}

CFRelease(authentication);

authentication = NULL;

// check for bad credentials (to be treated separately)

if (err.domain == kCFStreamErrorDomainHTTP &&

(err.error == kCFStreamErrorHTTPAuthenticationBadUserName

|| err.error == kCFStreamErrorHTTPAuthenticationBadPassword))

{

retryAuthorizationFailure(&authentication);

return;

} else {

errorOccurredLoadingImage(err);

}

}

清单4 - 4应用请求的身份验证对象

void resumeWithCredentials() {

// Apply whatever credentials we‘ve built up to the old request

if (!CFHTTPMessageApplyCredentialDictionary(request, authentication,

credentials, NULL)) {

errorOccurredLoadingImage();

} else {

// Now that we‘ve updated our request, retry the load

loadRequest();

}

}

在内存中保留凭证

如果你打算经常与一个认证服务器交流,值得重用凭证来避免提示用户多次服务器的用户名和密码。本节解释的变化应一次性使用身份验证代码(比如在处理身份验证)来存储凭证在内存中以便重用。

重用凭证,有三个数据结构更改你需要让你的代码。

1.创建一个可变的数组来保存所有身份验证对象。

CFMutableArrayRef authArray;

而不是:

CFHTTPAuthenticationRef authentication;

2.创建一个从身份验证对象映射到凭证使用字典。

CFMutableDictionaryRef credentialsDict;

而不是:

CFMutableDictionaryRef credentials;

3.保持这些结构都用来修改当前验证对象和当前凭证。

CFDictionaryRemoveValue(credentialsDict, authentication);

而不是:

CFRelease(credentials);

现在,创建HTTP请求后,寻找一个匹配的验证对象之前每个负载。一个简单的、非优化方法寻找合适的对象可以是清单4 - 5所示。

清单4 - 5寻找一个匹配的验证对象

CFHTTPAuthenticationRef findAuthenticationForRequest {

int i, c = CFArrayGetCount(authArray);

for (i = 0; i < c; i ++) {

CFHTTPAuthenticationRef auth = (CFHTTPAuthenticationRef)

CFArrayGetValueAtIndex(authArray, i);

if (CFHTTPAuthenticationAppliesToRequest(auth, request)) {

return auth;

}

}

return NULL;

}

如果身份验证数组有匹配的验证对象,然后检查凭证存储是否正确的凭证也可以。这样做可以防止你不得不再次提示用户输入用户名和密码。找凭证使用CFDictionaryGetValue函数如清单4 - 6所示。

清单4 - 6搜索凭证存储

credentials = CFDictionaryGetValue(credentialsDict, authentication);

然后应用匹配验证对象和凭证原始HTTP请求并重新发送它。

警告:不适用的凭证用于HTTP请求接收服务器之前的要求。自从你上次认证时服务器可能改变了,你可以创建一个安全风险。

这些变化,你应用程序将能够在内存中存储身份验证对象和凭证,以备未来使用。

保持持久性存储凭证

在内存中存储凭证可以防止用户不得不重新输入服务器的用户名和密码在这特定的应用程序启动。然而,当应用程序退出时,这些证书将被释放。为了避免失去凭据,将它们保存在一个持久化存储中,每个服务器的证书需要只生成一次。钥匙链是推荐的地方用来存储凭证。即使你可以有多个钥匙,这个文档是指用户的默认密钥链钥匙扣。使用钥匙链意味着身份验证信息,您还可以使用存储在其他应用程序上,试图访问同一台服务器,反之亦然。

存储和检索凭证的钥匙扣需要两个函数:一个用于查找凭证字典用于身份验证和一个用于保存凭证最近的请求。这些函数将本文档中声明为:

CFMutableDictionaryRef findCredentialsForAuthentication(

CFHTTPAuthenticationRef auth);

void saveCredentialsForRequest(void);

函数findCredentialsForAuthentication首先检查凭证字典存储在内存中凭证是否在本地缓存。清单4 - 6给出了如何实现这一点。

如果凭证不缓存到内存,然后搜索钥匙链。SecKeychainFindInternetPassword搜索钥匙链,使用函数。这个函数需要大量的参数。参数,和一个简短的描述他们是如何使用HTTP身份验证凭证,包括:

  keychainOrArray

  NULL 用来指定用户的默认密钥链列表。

  serverNameLength

  serverName的长度,通常strlen(serverName)。

serverName

    从HTTP请求服务器名称解析。

securityDomainLength

安全域的长度,或0如果没有域。在样本代码中,realm ? strlen(realm) : 0 传递到账户有两种情况。

securityDomain

身份验证的领域对象,从CFHTTPAuthenticationCopyRealm获得功能。

accountNameLength

accountName的长度。自从accountName是NULL的,这个值是0。

accountName

没有帐户名称获取密钥链项时,所以这应该是NULL。

pathLength

path的长度,或者为0如果没有 path。在样本代码中,path ? strlen(path) : 0传递到账户有两种情况。。

path

身份验证对象的路径,从CFURLCopyPath获得功能。

port

端口号,从函数CFURLGetPortNumber获得。

protocol

一个字符串代表协议类型,如HTTP或HTTPS。获得的协议类型是通过调用CFURLCopyScheme功能。

authenticationType

身份验证类型,从函数CFHTTPAuthenticationCopyMethod获得。

passwordLength

0,因为没有密码而获取密钥链项的时候是必要的。

passwordData

NULL,因为没有密码而获取密钥链项的时候是必要的。

itemRef

SecKeychainItemRef密钥链项引用对象,返回时找到正确的钥匙链条目。

当正确时,应该如清单4所示的代码。

清单4 - 7搜索钥匙链

didFind =

SecKeychainFindInternetPassword(NULL,

strlen(host), host,

realm ? strlen(realm) : 0, realm,

0, NULL,

path ? strlen(path) : 0, path,

port,

protocolType,

authenticationType,

0, NULL,

&itemRef);

假设SecKeychainFindInternetPassword成功返回,创建一个钥匙扣属性列表(SecKeychainAttributeList)包含一个钥匙扣属性(SecKeychainAttribute)。钥匙链的属性列表将包含用户名和密码。加载密钥链属性列表,调用函数SecKeychainItemCopyContent并将其传递给密钥链项引用对象(itemRef)被SecKeychainFindInternetPassword返回。这个函数将填补钥匙扣属性账户的用户名,和一个void
* *作为密码

用户名和密码可以用来创建一个新组凭证。清单4 - 8显示了这个过程。

清单4 - 8从钥匙链加载服务器凭证

if (didFind == noErr) {

SecKeychainAttribute     attr;

SecKeychainAttributeList attrList;

UInt32                   length;

void                     *outData;

// To set the account name attribute

attr.tag = kSecAccountItemAttr;

attr.length = 0;

attr.data = NULL;

attrList.count = 1;

attrList.attr = &attr;

if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData)

== noErr) {

// attr.data is the account (username) and outdata is the password

CFStringRef username =

CFStringCreateWithBytes(kCFAllocatorDefault, attr.data,

attr.length, kCFStringEncodingUTF8, false);

CFStringRef password =

CFStringCreateWithBytes(kCFAllocatorDefault, outData, length,

kCFStringEncodingUTF8, false);

SecKeychainItemFreeContent(&attrList, outData);

// create credentials dictionary and fill it with the user name & password

credentials =

CFDictionaryCreateMutable(NULL, 0,

&kCFTypeDictionaryKeyCallBacks,

&kCFTypeDictionaryValueCallBacks);

CFDictionarySetValue(credentials, kCFHTTPAuthenticationUsername,

username);

CFDictionarySetValue(credentials, kCFHTTPAuthenticationPassword,

password);

CFRelease(username);

CFRelease(password);

}

CFRelease(itemRef);

}

从钥匙链检索凭证是唯一有用的,你首先可以将凭证存储在钥匙链。非常相似的步骤加载证书。首先,看看凭证是否已经存储在钥匙链。联系SecKeychainFindInternetPassword,但通过用户名为accountnamelength的帐户名和帐户名的长度。

如果条目存在,修改它改变密码。钥匙链的数据字段属性设置为包含用户名,以便您修改正确的属性。然后调用函数SecKeychainItemModifyContent并通过密钥链项引用对象(itemRef),钥匙链属性列表,和新密码。通过修改密钥链项而不是重写,钥匙链上的条目将会正确地更新和任何相关的元数据仍将保留。入口应该类似于清单4 - 9。

清单4 - 9日修改密钥链项

// Set the attribute to the account name

attr.tag = kSecAccountItemAttr;

attr.length = strlen(username);

attr.data = (void*)username;

// Modify the keychain entry

SecKeychainItemModifyContent(itemRef, &attrList, strlen(password),

(void *)password);

如果条目不存在,那么您将需要从头开始创建它。函数SecKeychainAddInternetPassword完成这一任务。其SecKeychainFindInternetPassword参数是一样的,但与调用SecKeychainFindInternetPassword相比,你供应SecKeychainAddInternetPassword用户名和密码。释放后的密钥链项引用对象成功调用SecKeychainAddInternetPassword除非你需要使用别的东西。见清单4到10中的函数调用。

清单4到10存储一个新的密钥链项

SecKeychainAddInternetPassword(NULL,

strlen(host), host,

realm ? strlen(realm) : 0, realm,

strlen(username), username,

path ? strlen(path) : 0, path,

port,

protocolType,

authenticationType,

strlen(password), password,

&itemRef);

验证防火墙

验证防火墙非常类似于身份验证服务器除了必须检查每一个失败的HTTP请求代理身份验证和服务器身份验证。这意味着你需要单独的(本地和持久)代理服务器和源服务器。因此,失败的HTTP响应的过程将会:

确定响应的状态码407(一个代理的要求)。如果是,找到一个匹配验证对象和凭证通过检查当地代理存储和持久代理商店。如果这些都没有一个匹配的对象和凭证,然后从用户请求证书。认证对象应用于HTTP请求并再试一次。

确定响应的状态码401(一个服务器的挑战)。如果是,遵循相同的过程与一个407响应,但使用原始服务器的商店。

在使用代理服务器执行时也有一些细微的差别。首先,钥匙扣调用的参数来自代理主机和端口,而不是从一个源服务器的URL。第二个是,当要求用户输入用户名和密码,确保及时明确的密码是什么。

通过遵循这些说明,您的应用程序应该能够处理验证防火墙。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-15 13:18:26

iOS Communicating with Authenticating HTTP Servers 与HTTP服务器通信认证 官方文档翻译(六)的相关文章

ios Communicating with HTTP Servers 与HTTP服务器通信 官方文档翻译(五)

原文地址:developer 时间过的很快,一眨眼周末就过了.人的一生到底怎么样才能过的有意义呢?我一直在找答案.做自己喜欢的事情,爱自己喜欢的人,或是成就一番伟业.都是不错的选择,找到自己想要做的事情,不要放弃寻找.最终生命带走的只能是珍贵的回忆,晚上快乐. 与HTTP服务器通信 本章解释了如何创建.发送和接收HTTP请求和响应. 创建一个CFHTTP请求 组成一个HTTP请求消息的远程服务器执行的方法,对象操作(URL),消息头和消息体.方法通常是下列之一:GET, HEAD, PUT, P

与身份验证HTTP服务器通信(Communicating with Authenticating HTTP Servers)

本文描述了如何利用CFHTTPAuthentication API与需要身份验证的HTTP服务器通信.它解释了如何找到匹配的验证对象和证书,并将它们应用到HTTP请求,然后存储以供以后使用. 一般来说,如果一个HTTP服务器返回一个401或407响应你的HTTP请求,这表明服务器进行身份验证需要证书.在CFHTTPAuthentication API中,每个证书组存储在CFHTTPAuthentication 对象中.因此,每个不同的身份认证服务器和每个不同用户连接的服务器需要一个单独的CFHT

与HTTP服务器通信(Communicating with HTTP Servers)

本文解释了如何创建.发送和接收HTTP请求和响应. 创建一个CFHTTP请求 HTTP请求是一个消息,这个消息由远程服务器执行的方法,操作的对象(URL),消息头和消息体.方法通常是下面之一:GET, HEAD, PUT, POST, DELETE, TRACE, CONNECT 或OPTIONS.用CFHTTP创建一个HTTP请求分为四个步骤: 使用CFHTTPMessageCreateRequest 函数生成CFHTTP消息对象 使用CFHTTPMessageSetBody函数设置消息体 使

iOS CFNetwork Concepts CFNetwork概念 官方文档翻译(三)

原文地址:developer CFNetwork概念 CFNetwork是一个低级的.高性能的框架,使您能够详细的控制协议栈.BSD套接字是一个扩展,提供对象的标准套接字抽象API来简化任务,比如与FTP和HTTP服务器或沟通解决DNS主机.CFNetwork为基础,从主体上和理论上,BSD套接字. 正如CFNetwork依赖于BSD套接字,有大量的Cocoa类依赖CFNetwork(例如,NSURL).此外,Web工具包是一组Cocoa类在窗口中显示网页内容.这两个类是非常高的水平,实现大部分

iOS开发网络篇—发送json数据给服务器以及多值参数

iOS开发网络篇—发送json数据给服务器以及多值参数 一.发送JSON数据给服务器 发送JSON数据给服务器的步骤: (1)一定要使用POST请求 (2)设置请求头 (3)设置JSON数据为请求体 代码示例: 1 #import "YYViewController.h" 2 3 @interface YYViewController () 4 5 @end 6 7 @implementation YYViewController 8 9 - (void)viewDidLoad 10

客户端(android,ios)与服务器通信

android,ios客户端与服务器通信为了便于理解,直接用PHP作为服务器端语言 其实就是一个 http请求响应的过程序,先从 B/S模式说起浏览器发起http请求,服务器响应请求,并把数据返回给浏览器,经过浏览器的渲染,即为我们所看到的效果 客户端(android,ios)android通过 httpClient或urlconnection等网络请求去实现http请求,(相当于浏览器,发送 url请求),即访问服务器,服务器响应请求,并把结果返回给客户端,经过客户端渲染,即为我们看到的效果.

ios推送消息php做推送服务器

<?php /** * Main method to run the object * $message 消息内容 * $deviceToken 这里是iphone手机唯一的Token码(记得去掉空格) * $badge 就是应用图标右上角那个数字 * $sound 消息的声音 * $apnsCert 证书路径 * $passphrase 私钥的密码(可以不写) */ public function iosPush($message,$deviceToken,$badge=1,$sound='D

【iOS官方文档翻译】iOS蓝牙的基本概念

之前写了[iOS官方文档翻译]iOS的蓝牙连接.数据接收及发送一文,介绍了怎样进行蓝牙通讯,但是很多基本概念没有进行解释,看起来可能有点吃力,所以现在再翻译一篇苹果对官方蓝牙4.0一些基本概念介绍的文章. 1.中心设备和外围设备以及它们在蓝牙通讯中的角色. 在所有的BLE( Bluetooth low energy,下文简称蓝牙4.0 )通讯中都涉及2个主要的角色:中心设备和外围设备.它是基于传统的客户-服务器(主从式)结构,一般来说,外围设备有其它设备需要的数据,中心设备使用外围设备提供的数据

ios delegate 代理模式 观察者模式 不同视图间的通信

delegate,在ios中比比皆是,NSURLConnection(网络请求有),tableView, connectionView,等系统自带 的常见代理.甚至,自己写代码的时候,随意间敲打出了protocol,根据需求,就出来了个delegate.其实也就是观察者模式的具体应用.基本的observer.一样的思想,就是创建一个抽象方法,到具体的视图控制器中才去实现.最常见的做法是:创建单例模式,然后,调用对象,设置代理对象,写具体的实现. NSNotification通知信号虽然也能达到同