改定履历
2011-09-09---------------------新建文本文档
引文:
调试GLOOX 1.0的注册功能颇费了一些功夫。总体逻辑如GLOOX自带的例子一样是毫无疑问的,但是照搬例子又是不能完成注册的,返回错误码为4------RegistrationBadRequest。笔者一开始在网上狂搜解决方案,资料少之又少,有建议重写Client::handleNormalNode函数(目的是禁止SASL认证)的,有直接继承Client重写Client::handleNormalNode函数的,但都没说到点子上。经过一段时间的研究,在GLOOX的maillist上得到启发,顺利完成注册。现将解决方案记录下来:
环境
客户端:GLOOX1.0 VS2008
服务器:OPENFIRE 默认安装
对于GLOOX自带的注册例子不能正常注册的问题有人在邮件列表里提出来。一个哥们这样回答:
- Ok, I've found what the problem was
- In openFire server parameters, Anonymous Login => Disabled !!!
意思是要 禁用openFire服务器里的选项”注册和登录“的”匿名登录“项。
笔者按此说明禁用该选项,果然注册成功。
这说明开始的注册失败是和匿名登录有关系的。我们来看一下引用registration_expmple例子登录失败时的XML流:
S->C:服务器返回给客户端支持的认证机制:
- <stream:features xmlns:stream='http://etherx.jabber.org/streams'><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms><compression xmlns='http://jabber.org/features/compress'><method>zlib</method></compression><auth xmlns='http://jabber.org/features/iq-auth'/><register xmlns='http://jabber.org/features/iq-register'/></stream:features>
从上面XML流中我们可以看到,默认openFire支持四种认证机制,分别是:DIGEST-MD5、PLAIN、ANONYMOUS、CRAM-MD5。然后我们看GLOOX客户端的响应流:
C->S:客户端返回选择的认证方式:
- <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>
接下来的流程就是客户端”无耻“的选择了以匿名的方式登录了服务器,然后再发送注册请求,请求如下:
- <iq id='uid:4e69eccd:00006784' type='set' from='447e0585@zxl/447e0585' xmlns='jabber:client'><query xmlns='jabber:iq:register'><username>bbaxiao</username><password>123456</password><name>test2</name><email>163@gmail.com</email></query></iq>
我们看到,IQ节里包含“form”属性,即客户端匿名身份标识。
注意,一个客户端已经以一个身份(由服务器临时分配的一个JID)登录,建立了会话,在服务器上我们会看到这个会话,并且服务器发送心跳一直维护这个会话。这种情况下,这个客户端再发送注册请求(另一个身份)建立与服务器的连接是不被允许的。具体请参考XEP-0077(In-Band Registration):我们关注这两段:
- If the entity cancels its registration with its "home" server (i.e., the server at which it has maintained its XMPP account), then the entity SHOULD NOT include a 'from' or 'to' address in the remove request the server SHOULD then return a <not-authorized/> stream error and terminate all active sessions for the entity. The server SHOULD perform the remove based on the bare JID <localpart@domain.tld> associated with the current session or connection over which it received the remove request. If the server is an instant messaging and presence server that conforms to XMPP IM [8], the server SHOULD also cancel all existing presence subscriptions related to that entity (as stored in the entity's roster).
- If the entity cancels its registration with a service other than its home server, its home server MUST stamp a 'from' address on the remove request, which in accordance with XMPP Core will be the entity's full JID <localpart@domain.tld/resource>. The service MUST perform the remove based on the bare JID <localpart@domain.tld> portion of the 'from' address.
正常的注册流如下:
- <iq id='uid:4e69eccd:00003d6c' type='set' xmlns='jabber:client'><query xmlns='jabber:iq:register'><username>bbaxiao</username><password>123456</password><name>test2</name><email>163@gmail.com</email></query></iq>
---------------------------
综上所述,解决方案如下:
一、关闭openFire的匿名登录功能。^_^……
二、禁止GLOOX匿名认证功能。
- file:client.cpp
- fun: int Client::getSaslMechs( Tag* tag )
- line:355
- 将355行注释掉即可。
- 354:if( tag->hasChildWithCData( mech, "ANONYMOUS" ) )
- 355 //mechs |= SaslMechAnonymous;
重新编译生成DLL即可。
三、手动设置GLOOX客户端SASL认证机制
在调用j->connect()之前设置SASL认证机制,比如设置为“DIGEST-MD5”
- j->setSASLMechanisms(SaslMechDigestMd5);
四、根据XEP-0077所述,即使其名登录,注册流只要不带“from”属性应该也可以。所以我们要处理发出的注册流,去除“from”属性重新发送注册流即可。