由于此库使用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,kcfhttpAuthenticationSchemeDigest,/* 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;}
requestDescription内容是:
GET /digest-auth/auth/user/passwd http/1.1Host: httpbin.orgSec-WebSocket-Version: 13Upgrade: websocketSec-WebSocket-Key: 3P5YiQDt+g/wgxHe71Af5Q==Connection: UpgradeOrigin: http://httpbin.org/
chalengeDescription包含:
http/1.1 401 UNAUTHORIZEDServer: NginxContent-Type: text/HTML; charset=utf-8Set-cookie: fake=fake_valueAccess-Control-Allow-Origin: http://httpbin.org/Access-Control-Allow-Credentials: trueDate: Mon,29 Jun 2015 12:21:33 GMTProxy-Support: Session-Based-AuthenticationWww-Authenticate: Digest nonce="0c7479b412e665b8685bea67580cf391",opaque="4ac236a2cec0fc3b07ef4d628a4aa679",realm="me@kennethreitz.com",qop=authContent-Length: 0Connection: 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; scheme = <undecIDed>,forProxy = false}
我在文档中发现了这些值的含义:domain = kcfStreamErrorDomainhttp,error = kcfStreamErrorhttpAuthenticationTypeUnsupported(感谢@JensAlfke我在你的评论之前找到了它).为什么它不受支持?文档声称支持摘要有一个常量kcfhttpAuthenticationSchemeDigest,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 @ #xNsstring *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 UnauthorizedWww-Authenticate: Digest realm="testrealm@host.com",opaque="5ccc069c403ebaf9f0171e9517f40e41"authData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported2015-07-01 16:33:57.660 cfauthtest[24742:600143] testing header value: Digest nonce="b6921981b6437a4f138ba7d631bcda37",opaque="3de7d2bd5708ac88904acbacbbebc4a2",qop=authhttp/1.1 401 UnauthorizedWww-Authenticate: Digest nonce="b6921981b6437a4f138ba7d631bcda37",qop=authauthData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported2015-07-01 16:33:57.660 cfauthtest[24742:600143] testing header value: Basic realm="Fake Realm"http/1.1 401 UnauthorizedWww-Authenticate: Basic realm="Fake Realm"authData are INVALID error.domain=kcfStreamErrorDomainhttp error.error=kcfStreamErrorhttpAuthenticationTypeUnsupported
在我自己的回答后编辑:
替代解决方案
其他可能的解决方案是手动解析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 workaroundextern 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=02015-07-03 11:47:02.852 cfauthtest[42766:934054] testing header value: Digest nonce="b6921981b6437a4f138ba7d631bcda37",qop=authauthData are ValID error.domain=UnkNownDomain=0 error.error=UnkNownError=02015-07-03 11:47:02.852 cfauthtest[42766:934054] testing header value: Basic realm="Fake Realm"http/1.1 401 UnauthorizedWww-Authenticate: Basic realm="Fake Realm"authData are ValID error.domain=UnkNownDomain=0 error.error=UnkNownError=0
所以解决方法有效.
我将继续寻找仅使用公共API的其他解决方法.至少现在我知道问题是什么.
总结以上是内存溢出为你收集整理的ios – CFHTTPMessageAddAuthentication无法向请求添加身份验证数据全部内容,希望文章能够帮你解决ios – CFHTTPMessageAddAuthentication无法向请求添加身份验证数据所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)