Прохождение аутенфикации
Итак, первым действием при соединении с сервером Jabber, которым должен выполнить наш клиент — является аутенфикация. Аутенфикация будет происходить используя механизм SASL аутенфикации, описанный в в "RFC 2831 — Using Digest Authentication as a SASL Mechanism", алгоритм работы который будет рассмотрен подробнее, чуть далее.
Итак, мы установили физическое соединение с сервером, теперь нам нужно пройти аутенфикацию, для этого клиент посылает серверу следующий пакет:
<?xml version='1.0' encoding='UTF-8'?> <stream:stream to='jabber.ru' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xml:l='ru' version='1.0'>В ответ сервер пришлет подтверждение, о рукопожатии:
<?xml version='1.0'?> <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='3966489307' from='jabber.ru' version='1.0' xml:lang='en'>Сразу же после приема первого пакета, придет пакет, содержащий информацию о возможностях и доступных механизмах сервера. Данные возможности нужны, будут для полноценной работы с сервером:
<stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/> <compression xmlns='http://jabber.org/features/compress'> <method>zlib</method> </compression> <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism>DIGEST-MD5</mechanism> <mechanism>PLAIN</mechanism></mechanisms> <register xmlns='http://jabber.org/features/iq-register'/> </stream:features>Что мы видим в пакете, видим, что сервер поддерживает zip компрессию при передаче пакетов, поддерживает механизм аутенфикации DIGEST-MD5, и другие возможности. Стоит также отметить, что возможности сервера зависят от самого сервера и в зависимости от программы могут изменяться. Подробнее вы можете узнать в RFC 3920. Однако нас интересует то, что сервер поддерживает механизм аутенфикации DIGEST-MD5. Отлично, скажем мы и отправим ему пакет, говорящий о том, что мы хотим пройти аутенфикацию используя механизм DIGEST-MD5.
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>После получения данного пакета сервер присылает нам, так называемый challenge-пакет:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> bm9uY2U9IjIyNjQ3NzQ4Iixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz </challenge>Данный пакет мы должны будем разобрать. Как это сделать? Ранее внимательный читатель обратил внимание, что некоторые пакеты могут передаваться в кодировке Base64. Это наш случай. Текстовый элемент содержит информацию в данной кодировке, которая после раскодирования примет следующий вид:
nonce="22647748",qop="auth",charset=utf-8,algorithm=md5-sessИз этой строки нам понадобится значение Nonce для последующего построения ответа серверу, после чего мы подготавливаем строку ответа, которую мы передадим на сервер в ответном пакете, предварительно закодировав ее в Base64. Итак, ответная строка будет иметь следующий вид:
username="delphi-test", realm="jabber.ru", nonce="22647748", cnonce="2313e069649daa0ca2b76363525059ebd", nc=00000001, qop=auth, digest-uri="xmpp/jabber.ru" ,charset=utf-8, response=16351f86cc5591312e20b4ccd880eadbгде:
username — JID-node пользователя
realm — JID-domain пользователя
nonce — Уникальный код сессии, присланный нам ранее сервером
cnonce — Уникальный код ответной клиентской сессии, сгенерированный клиентом
nc — Так называемый once count — сколько раз был использован текущий nonce. Обычно значение параметра равно 00000001, его и будем использовать. На самом деле параметр довольно интересный и стоит отдельного рассмотрения и изучения в RFC, но как показала практика его смело можно игнорировать.
digest-uri — Протокол подключения, для XMPP сервера он состоит из соединения строк "xmpp/" + JID Domain
charset — поддержка кодировки пароля и имени, в нашем случае UTF-8
И самый важный параметр response в котором заключен ключ ответа серверу, включающий в себя пароль и ответные данные в формате MD5 строящийся по определенному алгоритму.
Алгоритм построения строки ответа и параметра Response более подробно мы рассмотрим далее в подразделе "RFC 2831 использование MD5-Digest аутенфикации в SASL". Пока примем к сведению, что текущее и следующие два действие относится уже к данному алгоритму.
Итак, строку ответа, мы сформировали, закодировали в Base64 и отправляем обратно серверу (всё это должно быть в одну строчку, но, чтобы страница не расползалась, разбито на несколько):
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> dXNlcm5hbWU9ImRlbHBoaS10ZXN0IixyZWFsbT0iamFiYmVyLnJ1Iixub25jZT0iMjI2ND c3NDgiLGNub25jZT0iMjMxM2UwNjk2NDlkYWEwY2EyYjc2MzYzNTI1MDU5ZWJkIixu Yz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2phYmJlci5ydSIsY2 hhcnNldD11dGYtOCxyZXNwb25zZT0xNjM1MWY4NmNjNTU5MTMxMmUyMGI0Y2Nk ODgwZWFkYg== </response>Если все нормально мы получим следующий ответ:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> cnNwYXV0aD1lOTg5NjZjZjUxNjliZWUzOTYzNGU5Zjk5ZTIzZDZhYg== </challenge>Тут нам особо ничего не нужно, подтверждаем принятие его, отправив со стороны клиента:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>И если все прошло успешно, то получаем со стороны сервера пакет, говорящий нам о том, что аутенфикация прошла успешно:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>Далее мы снова посылаем пакет рукопожатия:
<?xml version='1.0' encoding='UTF-8'?> <stream:stream to='jabber.ru' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xml:l='ru' version='1.0'>Получаем ответ:
<?xml version='1.0'?> <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='4096919146' from='jabber.ru' version='1.0' xml:lang='en'>После чего по стандарту мы должны связать нашего клиента с JID-ресурсом, что мы и делаем, посылая строку в формате UTF-8:
<iq type='set' id='bund_2'> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> <resource>тестовая</resource> </bind> </iq>Примечание: Все листинги будут представлены в ASCII формате, хотя на самом деле прием и посылка пакетов ведется в UTF-8. Однако что бы Вам не читать крякозаблы в листингах примеров, кодировка будет в показана в ASCII.
Сервер подтверждает связывание ресурса с данным клиентом:
<iq id='bund_2' type='result'> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> <jid>delphi-test@jabber.ru/тестовая</jid> </bind> </iq>Клиент посылает пакет присутствия в сети
<presence><show></show></presence>и все, аутенфикация пройдена, значок клиента в контакт-листе становится зелененьким, теперь он может посылать и принимать сообщения.