版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載楣富。
轉(zhuǎn)載請(qǐng)表明出處:http://www.reibang.com/p/049ef7d049eb
[TOC]
WTF簡(jiǎn)書(shū)不支持 toc 目錄模式,簡(jiǎn)單截圖一張汛骂。

在此為后面的smack學(xué)習(xí)做筆記竟终,以作備忘茎毁。
以下是本次采用的Demo環(huán)境:
- Openfire 4.3.8.2
- smack4.2.1
smack之登錄
xmpp首次登錄卧檐,可以通過(guò)自定義的Socket去進(jìn)行連接服務(wù)器墓懂,如果你服務(wù)器不是配置了非常特殊TLS連接,一般可以是用Smack中的XMPPConnection
類(lèi)建立連接(我推薦通過(guò)API中的XMPPConnection
去連接霉囚,能省去很多復(fù)雜的過(guò)程)捕仔,下面我講一下XMPPConnection
的連接配置。
基礎(chǔ)配置
XMPPConnection
的連接需要通過(guò)XMPPTCPConnectionConfiguration.builder()
配置你在Openfire設(shè)置的配置盈罐,代碼如下:
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
builder.setXmppDomain("server domain");
builder.setHostAddress(InetAddress.getByName("you host address"));
//default port 5222
builder.setPort(you port);
builder.setDebuggerEnabled(true);
builder.setCompressionEnabled(true);
builder.setSendPresence(false);
builder.setUsernameAndPassword("username", "password");
像如上代碼中的榜跌,XmppDomain是必須配置(這里的```XmppDomain```會(huì)拼接在首次建立```Socket```的IQ中的to和from中),HostAddress是你運(yùn)行服務(wù)器的域名或IP地址盅粪,Port是你登錄的端口钓葫,在Openfire中默認(rèn)是5222端口,但如果你的服務(wù)器使用的是Nginx做前置機(jī)票顾,那么這里你需要填入你的Nginx配置的端口础浮。
### 進(jìn)階配置
可能看到這里,你會(huì)覺(jué)得很奇怪库物,為什么還有個(gè)進(jìn)階配置霸旗,難道Openfire難道登錄就是一個(gè)普通的Socket登錄嗎?這樣很容易被黑吧戚揭,沒(méi)錯(cuò),實(shí)際上還有進(jìn)階配置滿(mǎn)足加密連接和自定義的要求撵枢,如TLS登錄或SSL登錄或者壓縮通訊民晒,如下例子:
* 如自定義的TLS登錄
TLS的登錄方式精居,具體筆者也沒(méi)實(shí)踐過(guò),歡迎拍磚潜必。
```Java
SSLContext sslContext = SSLContext.getInstance("TLS");
MemorizingTrustManager mtm = new MemorizingTrustManager(getApplicationContext());
MemorizingKeyManager memorizingKeyManager = new MemorizingKeyManager(getApplicationContext(), "123456");
sslContext.init(new X509KeyManager[]{(X509KeyManager) memorizingKeyManager}
, new X509TrustManager[]{mtm}, new java.security.SecureRandom());
builder.setCustomSSLContext(sslContext);
builder.setHostnameVerifier(mtm.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()));
以上的代碼需要注意一點(diǎn)是內(nèi)中的MemorizingTrustManager
,他來(lái)自于MemorizingTrustManager,而另外一個(gè)MemorizingKeyManager
是自定義的類(lèi)靴姿,內(nèi)中的代碼其實(shí)就是自定義了一個(gè)繼承自X509TrustManager
的類(lèi),這里不做深究磁滚,如有需要可以聯(lián)系我佛吓。
- 使用舊的TLS連接
實(shí)際上這里的意思是關(guān)閉安全模式,可以通過(guò)如下代碼關(guān)閉安全模式
* 其他的兼容方式
實(shí)際上就是沿用上面的那個(gè)函數(shù)```builder.setSecurityMode```中填入的另外兩種方式垂攘,以下是這三種方式的注釋和描述:
```JavaDoc
/**
* Security via TLS encryption is required in order to connect. If the server
* does not offer TLS or if the TLS negotiation fails, the connection to the server
* will fail.
*/
required,
/**
* Security via TLS encryption is used whenever it's available. This is the
* default setting.
* <p>
* <b>Do not use this setting</b> unless you can't use {@link #required}. An attacker could easily perform a
* Man-in-the-middle attack and prevent TLS from being used, leaving you with an unencrypted (and
* unauthenticated) connection.
* </p>
*/
ifpossible,
/**
* Security via TLS encryption is disabled and only un-encrypted connections will
* be used. If only TLS encryption is available from the server, the connection
* will fail.
*/
disabled
- 開(kāi)啟通訊壓縮
這里的通訊壓縮開(kāi)啟后维雇,傳輸?shù)牧髁繉⒐?jié)省90%
builder.setCompressionEnabled(true);
- SASL認(rèn)證
Openfire服務(wù)器默認(rèn)支持PLAIN 、ANONYMOUS 晒他、JIVE-SHAREDSECRET
吱型,這里我的服務(wù)器開(kāi)啟的是 PLAIN ,所以我使用 PLAIN
SASLAuthentication.blacklistSASLMechanism(SASLPlainMechanism.NAME);
登錄服務(wù)器
通過(guò)了上面的配置后陨仅,咱們可以登錄Openfire系統(tǒng)了立轧,相當(dāng)簡(jiǎn)單:
AbstractXMPPConnection xmpptcpConnection = new XMPPTCPConnection(builder.build());
if (!xmpptcpConnection.isConnected()) {
xmpptcpConnection.connect();
}else{
System.out.println("Already connected");
}
如果以上的配置和服務(wù)器的自定義不匹配牌柄,在建立連接的這一步會(huì)拋出異常,具體的異常可以通過(guò)對(duì)應(yīng)方案處理澈段,這里不詳談。
登錄底層報(bào)文通訊簡(jiǎn)要解析
作為程序猿栈顷,難道會(huì)使用API夠了嗎秋麸?不,我們繼續(xù)折騰猾警,下面一起來(lái)看一下底層的通訊報(bào)文流程孔祸。
- 在建立了Socket后,client會(huì)向服務(wù)器發(fā)出一條xml
SENT
<stream:stream
xmlns='jabber:client'
to='server domain'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'
from='username@server domain'
xml:lang='en'>
- 服務(wù)器解析到上面的指令后发皿,會(huì)返回用于告訴client可選的SASL方式
RECV
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams"
xmlns="jabber:client"
from="im"
id="c997c3a8"
xml:lang="en"
version="1.0">
<stream:features>
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">
</starttls>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>ANONYMOUS</mechanism>
<mechanism>JIVE-SHAREDSECRET</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>
- 客戶(hù)端選擇
ANONYMOUS
認(rèn)證方式
SENT
<auth
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
mechanism='ANONYMOUS'>=</auth>
- 服務(wù)器通過(guò)計(jì)算加密后的密碼后崔慧,服務(wù)器將返回
RECV
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
- 當(dāng)客戶(hù)端收到以上命令后,將首次發(fā)起連接的id發(fā)送到服務(wù)器
SENT
<stream:stream
xmlns='jabber:client'
to='server domain'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'
from='username@server domain'
id='c997c3a8'
xml:lang='en'>
- 這時(shí)服務(wù)器會(huì)返回如下內(nèi)容說(shuō)明此時(shí)的已經(jīng)成功綁定了當(dāng)前的Socket穴墅,
RECV
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream
xmlns:stream="http://etherx.jabber.org/streams"
xmlns="jabber:client"
from="im"
id="c997c3a8"
xml:lang="en"
version="1.0">
<stream:features>
<compression
xmlns="http://jabber.org/features/compress">
<method>zlib</method>
</compression>
<bind
xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
<session
xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</stream:features>
- 客戶(hù)端在接收到如上的內(nèi)容后會(huì)告訴服務(wù)器開(kāi)啟壓縮
SENT
<compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>
- 服務(wù)器返回
RECV
<compressed xmlns='http://jabber.org/protocol/compress'/>
- 客戶(hù)端收到服務(wù)器的響應(yīng)命令后惶室,重新建立一個(gè)Socket,發(fā)送指令
SENT
<stream:stream
xmlns='jabber:client'
to='server domain'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'
from='username@server domain'
id='c997c3a8'
xml:lang='en'>
- 服務(wù)器將返回玄货,不知道你有沒(méi)有發(fā)現(xiàn)皇钞,這里的
id="c997c3a8"
還是那個(gè)id
RECV
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream
xmlns:stream="http://etherx.jabber.org/streams"
xmlns="jabber:client"
from="im"
id="c997c3a8"
xml:lang="en"
version="1.0">
<stream:features>
<mechanisms
xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>ANONYMOUS</mechanism>
<mechanism>JIVE-SHAREDSECRET</mechanism>
</mechanisms>
<bind
xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
<session
xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</stream:features>
- 實(shí)際上到這里客戶(hù)端的登錄已經(jīng)完成了,但是還沒(méi)算成功松捉,接下來(lái)可以開(kāi)始做綁定Socket的操作了
SENT
<iq
id='b86j8-4'
type='set'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
</bind>
</iq>
- 服務(wù)器返回綁定了
JID = c997c3a8
的客戶(hù)端
RECV
<iq
type="result"
id="b86j8-4"
to="im/c997c3a8">
<bind
xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<jid>c997c3a8@im/c997c3a8</jid>
</bind>
</iq>
- 客戶(hù)端繼續(xù)請(qǐng)求,開(kāi)啟一個(gè)session
SENT
<iq id='b86j8-6' type='set'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>
- 這時(shí)服務(wù)器返回
RECV
<iq
type="result"
id="b86j8-6"
to="c997c3a8@im/c997c3a8"/>
- 到此夹界,整個(gè)登錄流程已經(jīng)成功了,接下來(lái)可以做一些用戶(hù)信息的獲取等操作隘世。
結(jié)尾
本篇帶領(lǐng)大家了解如何利用smack登錄Openfire和XMPP的報(bào)文流程可柿,下一個(gè)章節(jié)將會(huì)帶大家進(jìn)入xmpp的里面鸠踪,看看獲取用戶(hù)信息等具體的操作。
引用
https://download.igniterealtime.org/smack/docs/latest/documentation/gettingstarted.html/