Xmpp基本知识点

一、Xmpp资源绑定

      XMPP协议设计中引入了一个抽象的资源绑定过程,何为资源,如何绑定?
      首先这得从JID的格式设计说起,JID是XMPP前身Jabber协议ID的简写,用于唯一标识一个客户身份。一个合法的 JID 包括一组排列好的元素,包括域名(domain identifier),节点名(node identifier),和资源名(resource identifier),如下:
jid = [ node "@" ] domain [ "/" resource ] ,所有 JID 都是基于上述的结构,类似 { user@host/resource }这种结构。

  • node:是对用户的抽象,既可以代表一个真实的用户,也能表示一个虚拟用户如一个聊天室等。
  • domain:表达了客户所连接的服务器,在实践中通常表示一个特定的集群,由同一domain来表示。
  • resource:它通常表示一个特定的会话,连接。对于服务器和和其他客户端来说,资源名是不透明的。
          资源名的获得需要经历一个资源绑定的过程,这个过程按照XMPP协议约定是在SASL握手完成后,由客户端重新发起初始化流请求后。
    服务器向客户端声明资源绑定特性,过程如下:
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345' from='example.com' version='1.0'>  
   <stream:features>  
     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>  
   </stream:features>  

客户端发起资源绑定请求,并指定一个绑定的资源名

<iq type='set' id='bind_2'>  
     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>  
       <resource>pc-win-someone</resource>  
     </bind>  
</iq>  

服务端响应资源绑定请求,并返回绑定后的Full JID名

<iq type='result' id='bind_2'>  
     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>  
       <jid>somenode@example.com/pc-win-someone-server-gen-random-string</jid>  
     </bind>  
</iq>  

      以上过程即完成了资源绑定,那么资源绑定有什么作用呢,注意查看协议xml中客户端端请求绑定资源名为pc-win-someone,通常实现中可考虑用客户端的平台相关标识,例如 pc-win标示pc下的windows平台等,标识连接客户端的平台和自身名称,但XMPP协议约定resource由服务端按照每客户端生成随机值,用于唯一标识一个客户端一次连接会话。因此服务端的实现在客户端请求资源名后添加了随机生成的唯一后缀,用于区分不同的客户端连接。

那么如此设计的目的何在?

      主要考虑方便同账号用户的多点登陆(手机、pad、pc端等多点同时在线),通过resource区分同一用户的不同接入点,由node+domain+resource组成唯一的用户在线标识。通过用户ID形成一对多的用户接入映射,方便获得同一账号的多个接入信息,可灵活的设计多点登陆时用户的自选策略(是否踢下其他登陆、或选择最近登陆接收消息等)。

二、Xmpp安全机制

      XMPP(Extensible Messaging and Presence Protocol)是一个应用于实时通信的开放协议,定义了有关即时消息通信的各方面内容,本文主要是关于XMPP安全机制的介绍以及设计实现思考。

      XMPP包含一个保证流安全的方法来防止篡改和偷听,包括两个层次的安全机制,分别是TLS(Tansport Layer Security)和 SASL(Simple Authentication Security Layer)。
TLS主要用于保证传输通道安全,SASL用于用户鉴权认证,协商流程如下:

  1. 客户端发起,流初始化(建立TCP连接,发送如下格式数据)
<stream:stream  
     from='juliet@im.example.com'  
     to='im.example.com'  
     version='1.0'  
     xml:lang='en'  
     xmlns='jabber:client'  
     xmlns:stream='http://etherx.jabber.org/streams'>  
  1. 服务端应答流初始化
<stream:stream  
     from='im.example.com'  
     id='t7AMCin9zjMNwQKDnplntZPIDEI='  
     to='juliet@im.example.com'  
     version='1.0'  
     xml:lang='en'  
     xmlns='jabber:client'  
     xmlns:stream='http://etherx.jabber.org/streams'>  
  1. 服务端发送TLS流特征说明
<stream:features>  
     <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>  
         <required/>  
     </starttls>  
</stream:features>  
  1. 客户端发起TLS握手
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>  
  1. 服务端应答
    -- 握手成功,继续
<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>  

-- 握手失败,结束流,关闭TCP连接

<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>  
</stream:stream>  
  1. 握手成功后,客户端重新初始化加密流,并采用安全加密传输(通常由SSL实现),注意:这一步之后的交互数据全部经过加密传输TLS协商完成。
<stream:stream  
     from='juliet@im.example.com'  
     to='im.example.com'  
     version='1.0'  
     xml:lang='en'  
     xmlns='jabber:client'  
     xmlns:stream='http://etherx.jabber.org/streams'>  
  1. 服务端应答加密流初始化
<stream:stream  
     from='im.example.com'  
     id='vgKi/bkYME8OAj4rlXMkpucAqe4='  
     to='juliet@im.example.com'  
     version='1.0'  
     xml:lang='en'  
     xmlns='jabber:client'  
     xmlns:stream='http://etherx.jabber.org/streams'>  
  1. 服务端发送SASL特征说明,mechanism指出了服务端支持的认证机制,有关SASL认证的机制可参考RFC4422[html] [view plain]
<stream:features>  
     <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>  
       <mechanism>EXTERNAL</mechanism>  
       <mechanism>SCRAM-SHA-1-PLUS</mechanism>  
       <mechanism>SCRAM-SHA-1</mechanism>  
       <mechanism>PLAIN</mechanism>  
     </mechanisms>  
</stream:features>  
  1. 客户端选择认证机制
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AGp1bGlldAByMG0zMG15cjBtMzA=</auth>  

      以下认证过程根据选择的认证机制有所不同,实践中真正的实现一般就具体采用一种认证,依赖具体的用户权限系统进行设计。

这里说说其中一种常见Challenge-Response认证机制

      客户端在发送<auth>请求认证时,如上xml片段所示,<auth>元素中包含了一段BASE64编码的字符串,可以是用户ID(UID)向服务端表明身份id。
      服务端接收到认证请求后,发回挑战码,挑战码由服务器每次随机生成(挑战码也经过BASE64编码)

<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>  
   cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi  
   LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==  
</challenge>  

      客户端接收到挑战码后,根据用户输入的密码(原文)按注册用户时保存密码采用Hash算法进行同样的计算,得到与服务后端数据库存储的密码Hash同样的值,再以此为种子对挑战码进行特定算法计算。

具体算法可以用对称加密、二次加盐hash等,经过计算后的挑战码作为响应发回给服务端,如下:

<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>  
   dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i  
   T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw  
   MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i  
   LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo  
   YXJzZXQ9dXRmLTgK  
</response>  

服务端根据之前提供的UID获取用户保存的密码hash值,对响应码进行相同的算法计算后与客户端传递的挑战码响应进行碰撞认证
-- 成功,返回

<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>  

-- 失败,返回

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

结束流,并关闭TCP连接

三、Xmpp消息格式

      交换消息是XMPP的一个基本用途并且随之而来的是一个用户生成一个发给另一个实体的消息节。
XMPP定义的消息节语法完整格式如下:

<message  
    from='juliet@example.com/balcony'  
    to='romeo@example.net'  
    type='chat'  
    xml:lang='en'>  
  <subject>I implore you!</subject>  
  <subject xml:lang='cs'>  
    Úpěnlivě prosím!  
  </subject>  
  <body>Wherefore art thou, Romeo?</body>  
  <body xml:lang='cs'>  
     Pročež jsi ty, Romeo?  
  </body>  
  <thread parent='e0ffe42b28561960c6b12b944a092794b9683a38'>  
    0e3141cd80894871a68e6fe6b1ec56fa  
  </thread>  
</message>  
  • from属性:
          设置消息发送方自身的Full JID(node@domain/resource)
  • to属性:
          设置消息接收方的Bare JID(node@domain),通常第一次发送方无法确知接收方的Full JID,通过服务器中转路由时由服务器根据Base JID映射接收方的Full JID。 但如果这个消息是在回复之前接收到的消息,则to属性应该包含对方完整的Full JID。
          如此设计的好处在于:当to属性设定为Full JID时可以帮助服务器省却了接收者资源定位(接入定位),在一个IM服务集群环境中这种定位通常意味着一次分布式缓存读取操作。
  • type属性:XMPP约定了type的枚举值,包括:
          chat: 表明在一个点对点会话环境中的聊天消息。
          groupchat:表明在一个多人会话环境中的聊天消息。
          headline: 通常一些系统通知、警告、实时数据更新采用此类型,这类消息不期待客户端回复或响应,具有很高的实时性,不需要离线存储。
          normal: 默认的消息类型(缺乏type属性时),通常表达一种要求接收方必须确认的消息,一般用于系统提示强制用户确认或取消等。
          error: 表示一个错误消息,可能由服务端发送给客户端,也可能是另一个客户接收端回应给客户发送端,此类消息也不需要离线存储。
  • <subject>子元素:
          表明一个消息主题,通常客户端实现显示在聊天窗口标题栏处
  • <body>子元素:
    消息内容部分
  • <subject>和<subject>都允许包含多个元素标签,不同的标签根据xml:lang表达了不同的语言(XMPP可是一个国际化协议)
  • <thread>子元素:
          用于跟踪一个会话, 该元素的作用主要在于方便客户端实现消息展示(例如:消息历史查询时按每次会话折叠显示消息),每次会话产生一个唯一的thread id,xmpp推荐采用uuid算法,具体用法可参考XEP-0201扩展协议和RFC6121。

还有一种情况是离线消息,它与正常消息的格式和处理机制又有所不同,格式如下所示:

<message from='romeo@montague.net/orchard' to='juliet@capulet.com'>  
  <body>  
    O blessed, blessed night! I am afeard.  
    Being in night, all this is but a dream,  
    Too flattering-sweet to be substantial.  
  </body>  
  <delay xmlns='urn:xmpp:delay'  
     from='capulet.com'   
     stamp='2002-09-10T23:08:25Z'>Offline Storage</delay>  
</message>  

      离线消息中包含了一个<delay>的子元素,<delay>子元素的from记录了延迟消息的最后来源方,如上例中from为capulet.com指接收离线消息人连接的服务器,离线消息最终由该服务器发出stamp属性记录了离线消息的存储时间,客户端实现应显示该时间而非接收到的时间。

四、XMPP多用户文本聊天协议(MUC:Multi User Chat)

     &nbspXMPP在其XEP-0045扩展中定义了一个用于多用户文本会议(群聊)的协议,类似于聊天室、QQ群等。由于它作为一个标准协议在定义模型上力求完备,涵盖了现实中的绝大部分IM产品模型,而现实中的IM产品基本都只实现了XMPP定义的模型中的一个子集。

XMPP定义的一些基本概念:

  • 房间:房间的JID标识 room@service (例如, jdev@conference.jabber.org), 这里 "room" 是房间的名称而 "service" 是多用户聊天服务运行所在的主机名
  • 房客:房客的JID标识<room@service/nick>,nick是房客在房间的昵称
  • 岗位:表达了用户和房间的长期关系。XMPP定义的岗位有:所有者(owner)、管理者(admin)、成员(member)、排斥者(outcast)
  • 角色:表达了用户和房间的临时联系,它只存在与一次访问期间。XMPP定义的角色有:主持人(moderator)、与会者(paticipant)、游客(visitor)
    有关岗位、角色及其权限详细描述,参考协议规范描述(角色、岗位和权限)

XMPP MUC协议扩展定义了一个广泛的用例集合,下面提取一些典型的核心场景来简要分析说明并辅助实现。

  1. MUC服务发现
    主要用于客户端向服务器咨询是否支持MUC,协议交互细节详见:MUC Discovering
  2. 新建房间
    从房间创建的视角来看,本质上有2种类型的房间:
    instant room 临时房间(类似于临时会话),适用于那些临时选取多个用户进行会话的场景
    reserverd room 永久房间(类似于固定群)
  3. 销毁房间
    销毁房间通常仅限于房间的所有者,临时房间通常是在房间所有用户都离开后自动销毁
  4. 加入房间
    加入房间可以有2种方式,申请和邀请
  5. 发言
    在房间内发言方式从使用场景的角度看通常有3种:
  • 向房间内所有人发言,发言者发送一个消息类型为groupchat的消息,由房间服务转发给所有与会者。
  • 向部分人发言,这个场景发言者实际创建了一个临时房间,在该临时房间内进行群发。
  • 向某一个人发送似有消息,这个场景退化为了一对一的单独聊天。
  1. 退出房间
    主动退出、管理员(主持人)踢出房间

关于XMPP多用户文本聊天协议的完整用例集合,请参考协议规范。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • 关于XMPP最权威的讲解:http://www.jabbercn.org/RFC3920(这个才是最权威的,下面文...
    随风飘荡的小逗逼阅读 1,461评论 1 5
  • 要学习基于XMPP协议的IM开发,首先要熟悉XMPP协议本身。 XMPP协议的组成主要的XMPP 协议范本及当今应...
    RichieQ阅读 1,889评论 0 6
  • XMPP简介 XMPP协议简介 XMPP协议(Extensible Messaging and PresenceP...
    不规则先生阅读 6,292评论 2 31
  • 前面关于即时通讯基础Socket,大家学习使用XMPP之前可以先看看即时通讯系列之Socket简介 前言 前段时间...
    音符上的码字员阅读 4,197评论 3 16