基于xmpp协议的开源框架androidpn学习笔记

android服务端框架:SpringMVC+Hibernate+Tomcat(jetty)

用到工具包:Mina,  Tinder,  Ecache

Androidpn 服务端类图


时序图

Androidpn服务器端启动流程,如下图所示

 

服务器端的消息处理流程(消息接收和消息推送)

 

用户未注册消息处理流程(时序图)

 

 

 

对照rf3920文档可以看到如下会话内容:

一客户端与服务端建立联接:

1: 客户端初始化流给服务器:

<stream:stream to="10.0.2.2" xmlns="jabber:client"xmlns:stream="http://etherx.jabber.org/streams"version="1.0">

 

2: 服务器发送一个流标签给客户端作为应答:

<?xml version='1.0'encoding='UTF-8'?><stream:streamxmlns:stream="http://etherx.jabber.org/streams"xmlns="jabber:client" from="127.0.0.1" id="c8d0ff7c"xml:lang="en" version="1.0">

 

3: 服务器发送 STARTTLS 范围给客户端(包括验证机制和任何其他流特性):

<stream:features><starttlsxmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><authxmlns="http://jabber.org/features/iq-auth"/><registerxmlns="http://jabber.org/features/iq-register"/></stream:features>

 

4: 客户端发送 STARTTLS 命令给服务器:

<starttlsxmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

 

5: 服务器通知客户端可以继续进行:

<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

(或者): 服务器通知客户端 TLS 握手失败并关闭流和TCP连接:

<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

</stream:stream>

 

6客户端和服务器尝试通过已有的TCP连接完成 TLS 握手.

 (或者): 如果 TLS 握手不成功, 服务器关闭TCP 连接.

<stream:stream to="127.0.0.1" xmlns="jabber:client"xmlns:stream="http://etherx.jabber.org/streams"version="1.0">

 

7: 服务器发送一个流头信息应答客户端,其中包括任何可用的流特性:

<?xml version='1.0'encoding='UTF-8'?><stream:streamxmlns:stream="http://etherx.jabber.org/streams"xmlns="jabber:client" from="127.0.0.1"id="c8d0ff7c" xml:lang="en" version="1.0"><stream:features><authxmlns="http://jabber.org/features/iq-auth"/><registerxmlns="http://jabber.org/features/iq-register"/></stream:features>

 

TLS的定义

保证流安全的方法来防止篡改和偷听.这个传输层安全协议[TLS]的频道加密方法, 模拟了类似的其他"STARTTLS"(见RFC 2595 [USINGTLS])的扩展,如 IMAP [IMAP], POP3 [POP3], and ACAP [ACAP]."STARTTLS"的扩展名字空间是'urn:ietf:params:xml:ns:xmpp-tls'.

 

 

二用户注册:

1客户端发送注册指令

<iq id="12Sf7-0"type="set"><query

 

xmlns="jabber:iq:register"><password>86615c6b98f7454bb26d92af5fcf139c</password><username>0b3e76b776314187b79b6a63b124ef13</username></query></iq>

2 服务端发送注册完成响应指令

<iq type="result"id="12Sf7-0" to="127.0.0.1/a821f04e"/>

 

 

三用户登录:

1 客户端发送查询验证用户指令:

<iq id="12Sf7-1"type="get"><query

 

xmlns="jabber:iq:auth"><username>0b3e76b776314187b79b6a63b124ef13</username></query></iq>

2 服务端发送查询验证用户响应:

<iq type="result" id="12Sf7-1"><query

 

xmlns="jabber:iq:auth"><username>0b3e76b776314187b79b6a63b124ef13</username><password/><digest/><resource/></query></iq>

 

3 客户端发送指令:

 <iq id="12Sf7-2"type="set"><query

 

xmlns="jabber:iq:auth"><username>0b3e76b776314187b79b6a63b124ef13</username><digest>774b8c076e346766ff71ef39d5ac41763e97fd91</digest><resource>AndroidpnClient</resourc

 

e></query></iq>

 

4 服务端发送用户的JID响应:

<iq type="result"id="12Sf7-2"

 

to="0b3e76b776314187b79b6a63b124ef13@127.0.0.1/AndroidpnClient"/>

 

5 客户端发送:

查询用户名录,程序未作处理

<iq id="12Sf7-3"type="get"><query xmlns="jabber:iq:roster"></query></iq>

设置广播机制

<presenceid="12Sf7-4"></presence>

 

问题:

SSLConfig不能正常加载:

{ERROR} [2016-12-25 15:06:57,981]<org.androidpn.server.xmpp.net.XmppIoHandler> :java.lang.NoClassDefFoundError: Could not initialize classorg.androidpn.server.xmpp.ssl.SSLConfig

解决办法:

修改SSLConfig.java文件,示例代码如下:

....

private SSLConfig(){

 

      classPath = SSLConfig.class.getResource("/");

      storeType = Config.getString("xmpp.ssl.storeType","JKS");

      keyStoreLocation = Config.getString("xmpp.ssl.keystore","conf" + File.separator +"security" + File.separator +"keystore");

      keyStoreLocation =classPath.getPath() +File.separator +keyStoreLocation;

      keyPass = Config.getString("xmpp.ssl.keypass","changeit");

      trustStoreLocation = Config.getString("xmpp.ssl.truststore","conf" + File.separator +"security" + File.separator +"truststore");

      trustStoreLocation =classPath.getPath() +File.separator +trustStoreLocation;

      trustPass = Config.getString("xmpp.ssl.trustpass","changeit");

 

 

      log.debug("keyStoreLocation=" +keyStoreLocation);

      log.debug("trustStoreLocation=" +trustStoreLocation);

 

      // Load keystore

      try {

         keyStore = KeyStore.getInstance(storeType);

         keyStore.load(new FileInputStream(keyStoreLocation),keyPass.toCharArray());

      } catch (Exception e) {

         log.error("SSLConfig startup problem.\n" +"  storeType: [" + storeType +"]\n" + "  keyStoreLocation:[" +keyStoreLocation + "]\n"

                + "  keyPass: [" +keyPass + "]", e);

         keyStore = null;

      }

 

      // Load truststore

      try {

         trustStore = KeyStore.getInstance(storeType);

         trustStore.load(new FileInputStream(trustStoreLocation),trustPass.toCharArray());

 

      } catch (Exception e) {

         try {

            trustStore = KeyStore.getInstance(storeType);

            trustStore.load(null,trustPass.toCharArray());

         } catch (Exception ex) {

            log.error("SSLConfig startup problem.\n" +"  storeType: [" + storeType +"]\n" + " trustStoreLocation: [" +trustStoreLocation

                   + "]\n" + "  trustPass: [" + trustPass +"]", e);

            trustStore =null;

         }

      }

 

      // Init factory

      try {

         sslContext = SSLContext.getInstance("TLS");

 

         KeyManagerFactorykeyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

         keyFactory.init(keyStore,SSLConfig.getKeyPassword().toCharArray());

 

         TrustManagerFactoryc2sTrustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

         c2sTrustFactory.init(trustStore);

 

         sslContext.init(keyFactory.getKeyManagers(),c2sTrustFactory.getTrustManagers(),newjava.security.SecureRandom());

 

      } catch (Exception e) {

         log.error("SSLConfig factory setup problem." +"  storeType: [" + storeType +"]\n" + "  keyStoreLocation:[" +keyStoreLocation + "]\n"

                + "  keyPass: [" +keyPass + "]\n" + " trustStoreLocation: [" + trustStoreLocation + "]\n" + "  trustPass: [" + trustPass +"]",

                e);

         keyStore = null;

         trustStore =null;

      }

 

   }

 

   public static SSLConfig getInstance() {

      if (instance ==null) {

         synchronized (SSLConfig.class) {

            instance =new SSLConfig();

         }

      }

      return instance;

   }

....

修改Connection.java文件中对SSLConfig的引用

SSLConfig.getInstance();

KeyStore ksKeys = SSLConfig.getKeyStore();

 

修改SSLKeyManagerFactory.java文件同上

修改SSLTrustManagerFactory.java文件同上

 

 

重启服务器,查看日志输出如下:

{DEBUG} [2016-12-26 15:59:44,035]<org.androidpn.server.xmpp.net.XmppIoHandler> : messageReceived()...

{DEBUG} [2016-12-26 15:59:44,035] <org.androidpn.server.xmpp.net.XmppIoHandler>: RCVD: <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

{DEBUG} [2016-12-26 15:59:44,042]<org.androidpn.server.xmpp.net.Connection> : startTLS()...

{DEBUG} [2016-12-26 15:59:44,051] <org.androidpn.server.xmpp.ssl.SSLConfig>:keyStoreLocation=/D:/tools/service/apache-tomcat-7.0.2/webapps/ROOT/WEB-INF/classes/\conf/security/keystore

{DEBUG} [2016-12-26 15:59:44,052]<org.androidpn.server.xmpp.ssl.SSLConfig> :trustStoreLocation=/D:/tools/service/apache-tomcat-7.0.2/webapps/ROOT/WEB-INF/classes/\conf/security/truststore

{DEBUG}[2016-12-26 15:59:44,443] <org.androidpn.server.xmpp.net.Connection> :SENT: <proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

 

 

 

客户端添加tls

 

客户端在开启服务建立联接时报错信息

12-26 08:17:09.156: W/System.err(547):java.security.KeyStoreException: KeyStore jks implementation not found

12-26 08:17:09.166:W/System.err(547):    atjava.security.KeyStore.getInstance(KeyStore.java:119)

12-26 08:17:09.166:W/System.err(547):    atorg.jivesoftware.smack.ServerTrustManager.<init>(ServerTrustManager.java:61)

12-26 08:17:09.166:W/System.err(547):    atorg.jivesoftware.smack.XMPPConnection.proceedTLSReceived(XMPPConnection.java:759)

12-26 08:17:09.166:W/System.err(547):    atorg.jivesoftware.smack.PacketReader.parsePackets(PacketReader.java:267)

12-26 08:17:09.166:W/System.err(547):    atorg.jivesoftware.smack.PacketReader.access$1(PacketReader.java:220)

12-26 08:17:09.166:W/System.err(547):    atorg.jivesoftware.smack.PacketReader$1.run(PacketReader.java:70)

 

解决方法如下:

在connectTask方法中添加如下内容

    connConfig.setReconnectionAllowed(true);                 connConfig.setTruststorePath("system/etc/security/cacerts.bks");

    connConfig.setTruststorePassword("changeit");

    connConfig.setTruststoreType("bks");

 

客户端在关闭服务后,服务端报错:  (未解决不影响正常用运)

javax.net.ssl.SSLException: Inbound closed before receivingpeer's close_notify: possible truncation attack?

  

 

服务器端:发送广播消息和发送指定用户接收消息中的离线用户消息处理(未测试)

服务器重启后,在线用户session状态

客户端: 用户重启机器,服务的运行状态(未测试)

退出重新进入应用不能正常联接(断开不能重联)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值