大佬教程收集整理的这篇文章主要介绍了ios – CFHTTPMessageAddAuthentication无法向请求添加身份验证数据,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
由于此库使用CFNetwork
CFHTTPMessage*
API进行http功能(需要启动Web套接字连接),我正在尝试使用此API来提供身份验证.
有完全匹配的功能:CFhttpmessageAddAuthentication,但它不能像我期望的那样工作(据我所知documentation).
- (CFhttpmessageRef)createAuthenticationHandShakerequest: (CFhttpmessageRef)chalengemessage { CFhttpmessageRef request = [self createHandshakerequest]; BOOL result = CFhttpmessageAddAuthentication(request,chalengemessage,(__bridge CFStringRef)self.credentialS.User,(__bridge CFStringRef)self.credentials.password,kcfhttpAuthenticationscheR_400_11845@eDigest,/* I've also tried NULL for use strongest supplied authentication */ NO); if (!result) { NSString *chalengeDescription = [[NSString alloc] initWithData: CFBridgingRelease(CFhttpmessageCopyserializedmessage(chalengemessagE)) encoding: NSUTF8StringEncoding]; NSString *requestDescription = [[NSString alloc] initWithData: CFBridgingRelease(CFhttpmessageCopyserializedmessage(request)) encoding: NSUTF8StringEncoding]; SRFastLog(@"Failed to add authentication data `%@` to a request:\n%@After a chalenge:\n%@",self.credentials,requestDescription,chalengeDescription); } return request; }
GET /digest-auth/auth/user/passwd http/1.1 Host: httpbin.org Sec-WebSocket-Version: 13 Upgrade: websocket Sec-WebSocket-Key: 3P5YiQDt+g/wgxHe71Af5Q== Connection: Upgrade Origin: http://httpbin.org/
chalengeDescription包含:
http/1.1 401 UNAUTHORIZED Server: Nginx Content-Type: text/html; charset=utf-8 Set-Cookie: fake=fake_value Access-Control-Allow-Origin: http://httpbin.org/ Access-Control-Allow-Credentials: true Date: Mon,29 Jun 2015 12:21:33 GMT Proxy-Support: Session-Based-Authentication Www-Authenticate: Digest nonce="0c7479b412e665b8685bea67580cf391",opaque="4ac236a2cec0fc3b07ef4d628a4aa679",realm="me@kennethreitz.com",qop=auth Content-Length: 0 Connection: keep-alive
用户和密码值有效(“user”“passwd”).
为什么CFhttpmessageAddAuthentication返回NO?不知道问题是什么.我也尝试使用凭据更新空请求,但没有运气.
我已经使用http://httpbin.org/进行测试(在此步骤中,Web套接字的功能无关紧要).
请注意,使用过的代码不会使用(并且永远不会)NSURLrequst或NSURLSession或NSURLConnection /
我尝试使用不同的函数:CFhttpAuthenticationCreateFromResponse和CFhttpmessageApplyCredentials,结果相同.
至少CFhttpmessageApplyCredentials以CFStreamError的形式返回一些错误信息.问题是这个错误信息是无用的:error.domain = 4,error.error = -1000这些值没有记录在任何地方.
唯一记录的值如下所示:
typedef CF_ENUM(CFIndex,CFStreamErrorDomain) { kcfStreamErrorDomainCustom = -1L,/* custom to the kind of stream in question */ kcfStreamErrorDomainPOSIX = 1,/* POSIX errno; interpret using <sys/errno.h> */ kcfStreamErrorDomainMacOSStatus /* OSStatus type from Carbon APIs; interpret using <MacTypes.h> */ };
CFhttpAuthenticationCreateFromResponse返回无效对象,该描述返回:
<CFhttpAuthentication 0x108810450>{state = Failed; scheR_400_11845@e = <undecided>,forProxy = falsE}
我在文档中发现了这些值的含义:domain = kcfStreamErrorDomainhttp,error = kcfStreamErrorhttpAuthenticationTypeUnsupported(感谢@jensAlfke我在你的评论之前找到了它).为什么它不受支持?文档声称支持摘要有一个常量kcfhttpAuthenticationscheR_400_11845@eDigest,CFhttpmessageAddAuthentication接受并期望它!
我已经挖掘了source code of CFNetwork
authentication并试图找出问题所在.
我必须犯一些错误,因为这个简单的tast应用程序也会失败:
#import <Foundation/Foundation.h> #import <CFNetwork/CFNetwork.h> static NSString * const khttpAuthHeaderName = @"WWW-Authenticate"; static NSString * const khttpDigestChALLENgeExample1 = @"Digest realm=\"testrealm@host.com\"," "qop=\"auth,auth-int\"," "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; static NSString * const khttpDigestChALLENgeExample2 = @"Digest nonce=\"b6921981b6437a4f138ba7d631bcda37\"," "opaque=\"3de7d2bd5708ac88904ACBACBbebc4a2\"," "realm=\"me@kennethreitz.com\"," "qop=auth"; static NSString * const khttpBasicChALLENgeExample1 = @"Basic realm=\"Fake Realm\""; #define RETURN_StriNG_IF_CONSTANT(a,X) if ((a) == (X)) return @ #x NSString *NSStringFromCFErrorDomain(CFIndex domain) { RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainhttp); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainFTp); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainSSL); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainSystemConfiguration); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainSOCKS); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainPOSIX); RETURN_StriNG_IF_CONSTANT(domain,kcfStreamErrorDomainMacOSStatus); return [NSString StringWithFormat: @"UnkNownDomain=%ld",domain]; } NSString *NSStringFromCFErrorError(SInt32 error) { RETURN_StriNG_IF_CONSTANT(error,kcfStreamErrorhttpAuthenticationTypeUnsupported); RETURN_StriNG_IF_CONSTANT(error,kcfStreamErrorhttpAuthenticationBadUserName); RETURN_StriNG_IF_CONSTANT(error,kcfStreamErrorhttpAuthenticationBadpassword); return [NSString StringWithFormat: @"UnkNownError=%d",(int)error]; } NSString *NSStringFromCFhttpmessage(CFhttpmessageRef messagE) { return [[NSString alloc] initWithData: CFBridgingRelease(CFhttpmessageCopyserializedmessage(messagE)) encoding: NSUTF8StringEncoding]; } void testAuthenticationHeader(NSString *authenticatiohHeader) { CFhttpmessageRef response = CFhttpmessageCreateResponse(kcfAllocatorDefault,401,NULL,kcfhttpVersion1_1); CFAutorelease(responsE); CFhttpmessageSetHeaderFieldValue(response,(__bridge CFStringRef)khttpAuthHeaderName,(__bridge CFStringRef)authenticatiohHeader); CFhttpAuthenticationRef authData = CFhttpAuthenticationCreateFromResponse(kcfAllocatorDefault,responsE); CFAutorelease(authData); CFStreamError error; BOOL validAuthData = CFhttpAuthenticationIsValid(authData,&error); NSLog(@"tesTing header value: %@\n%@authData are %@ error.domain=%@ error.error=%@\n\n",authenticatiohHeader,NSStringFromCFhttpmessage(responsE),validAuthData?@"Valid":@"INVALID",NSStringFromCFErrorDomain(error.domain),NSStringFromCFErrorError(error.error)); } int main(int argc,const char * argv[]) { @autoreleasepool { testAuthenticationHeader(khttpDigestChALLENgeExample1); testAuthenticationHeader(khttpDigestChALLENgeExample2); testAuthenticationHeader(khttpBasicChALLENgeExample1); } return 0; }
日志显示:
2015-07-01 16:33:57.659 cfauthtest[24742:600143] tesTing header value: Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",opaque="5ccc069c403ebaf9f0171e9517f40e41" http/1.1 401 Unauthorized Www-Authenticate: Digest realm="testrealm@host.com",opaque="5ccc069c403ebaf9f0171e9517f40e41" authData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported 2015-07-01 16:33:57.660 cfauthtest[24742:600143] tesTing header value: Digest nonce="b6921981b6437a4f138ba7d631bcda37",opaque="3de7d2bd5708ac88904ACBACBbebc4a2",qop=auth http/1.1 401 Unauthorized Www-Authenticate: Digest nonce="b6921981b6437a4f138ba7d631bcda37",qop=auth authData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported 2015-07-01 16:33:57.660 cfauthtest[24742:600143] tesTing header value: Basic realm="Fake Realm" http/1.1 401 Unauthorized Www-Authenticate: Basic realm="Fake Realm" authData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported
在我自己的回答后编辑:
替代@L_450_63@案
其他可能的@L_450_63@案是手动解析WWW-Authenticate响应头并进行它并为新请求生成Authorization头.
是否有一些我可以在商业应用程序中使用的简单库或示例代码(只有这个)?我能做到这一点,但这需要宝贵的时间.赏金仍然可用:).
Apple CFNetwork API很糟糕
问题是CFhttpmessageRef中的响应具有隐藏的属性URl.
您可以阅读它:CFhttpmessageCopyrequestuRL未设置它,并且需要从CFhttpmessageRef正确创建身份验证对象.如果URL属性为空,则身份验证将失败.
那么为什么有些案例的响应与身份验证质询包含URL在其他情况下不是?
此工作响应来自CFReadStreamCreateForhttprequest创建的CFReadStreamRef作为此流的属性. Here is crappy example.因此,由于SocketRocket不使用CFReadStreamCreateForhttprequest,这是一个无法简单克服的大问题.
令人遗憾的是,CFhttpmessageAddAuthentication可以从请求修改的URL获取此URL,如果在响应中找不到它.
在这个问题上有完美的解决方法!但它涉及使用私有API(所以很可能它不会通过Apple审查).以下是完整的示例代码及其解决方法(与问题相同,但应用此解决方法),解决方法只需两行:公开私有API并使用它.
#import <Foundation/Foundation.h> #import <CFNetwork/CFNetwork.h> static NSString * const khttpAuthHeaderName = @"WWW-Authenticate"; static NSString * const khttpDigestChALLENgeExample1 = @"Digest realm=\"testrealm@host.com\",(int)error]; } NSString *NSStringFromCFhttpmessage(CFhttpmessageRef messagE) { return [[NSString alloc] initWithData: CFBridgingRelease(CFhttpmessageCopyserializedmessage(messagE)) encoding: NSUTF8StringEncoding]; } // exposing private API for workaround extern void _CFhttpmessageSetResponseURL(CFhttpmessageRef,CFURLRef); void testAuthenticationHeader(NSString *authenticatiohHeader) { CFhttpmessageRef response = CFhttpmessageCreateResponse(kcfAllocatorDefault,kcfhttpVersion1_1); CFAutorelease(responsE); // workaround: use of private API _CFhttpmessageSetResponseURL(response,(__bridge CFURLRef)[NSURL URLWithString: @"http://some.test.url.com/"]); CFhttpmessageSetHeaderFieldValue(response,const char * argv[]) { @autoreleasepool { testAuthenticationHeader(khttpDigestChALLENgeExample1); testAuthenticationHeader(khttpDigestChALLENgeExample2); testAuthenticationHeader(khttpBasicChALLENgeExample1); } return 0; }
日志结果如下:
2015-07-03 11:47:02.849 cfauthtest[42766:934054] tesTing header value: Digest realm="testrealm@host.com",opaque="5ccc069c403ebaf9f0171e9517f40e41" authData are Valid error.domain=UnkNownDomain=0 error.error=UnkNownError=0 2015-07-03 11:47:02.852 cfauthtest[42766:934054] tesTing header value: Digest nonce="b6921981b6437a4f138ba7d631bcda37",qop=auth authData are Valid error.domain=UnkNownDomain=0 error.error=UnkNownError=0 2015-07-03 11:47:02.852 cfauthtest[42766:934054] tesTing header value: Basic realm="Fake Realm" http/1.1 401 Unauthorized Www-Authenticate: Basic realm="Fake Realm" authData are Valid error.domain=UnkNownDomain=0 error.error=UnkNownError=0
所以解决方法有效.
我将继续寻找仅使用公共API的其他解决方法.至少现在我知道问题是什么.
以上是大佬教程为你收集整理的ios – CFHTTPMessageAddAuthentication无法向请求添加身份验证数据全部内容,希望文章能够帮你解决ios – CFHTTPMessageAddAuthentication无法向请求添加身份验证数据所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。