Openfire好友关系解决方案
今天是真把RFC-6121磕下来了,明确几个概念。
首先xmpp中的roster和出席订阅是两回事。roster的关系更像微博的订阅,不需要对方同意即可完成。而出席信息则“有点儿”像加好友,需要对方批准才能获取信息。
roster是可以独立于订阅状态存在的,因此只使用“花名册”功能的话,roster属性中的subscribe值是none。只有要求订阅对方的出席状态,subscribe值才会出现from、to、both。网上关于openfire的from、to、both、none关系文章太多了,而他们对应在openfire的roster表中sub、ask、recv状态关系也不胜枚举,就不再赘述了。
我们主要想解决一个类似QQ的加好友流程。 User A 发送好友请求至 User B, User B点击同意后,与User A 成为好友(subscribe状态变成both)。
解决方案如下,其中红色的部分是重点关注的地方,也是客户端和服务器需要修改的工作。
以上解决方案用到了RFC-6121 第3.4节中提到的预批准被订阅请求协议,openfire在[3.9.1版本](http://issues.igniterealtime.org/browse/OF-738)已经支持了这个特性。
因此,我们需要做的只是:
- 客户端自动同意订阅请求
- 客户端发出订阅请求包后,发送预批准包至服务器
-
服务器升级至3.9.1 - 更多的测试
当然,也可以通过重写RosterProvider的方式整个把Roster关系替换掉。不过我觉得,在遵从xmpp协议,并且之前已经有很多接口调用的情况下,这个方式还是比较值得考虑的。
PS:我的图画的是有多糟糕啊T^T
update
看样子openfire还没释放以上特性,我们可以通过插件形式完成以上操作。
核心代码如下:
@Override
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
throws PacketRejectedException {
if (session != null) {
log.info("intercept packet from:" + packet.getFrom() + ",to:" + packet.getTo());
}
if (packet != null) {
//只处理出席信息。
if (packet instanceof Presence) {
Presence presence = (Presence) packet;
//处理订阅类的出席信息,会接收到两次出席信息,只处理resource不为空的那次。
if (Presence.Type.subscribe.equals(presence.getType())) {
if (presence.getFrom() != null && !StringUtils.isEmpty(presence.getFrom().getResource())) {
Presence subscribedPresence = new Presence();
subscribedPresence.setType(Presence.Type.subscribed);
subscribedPresence.setTo(presence.getFrom().toBareJID());
subscribedPresence.setFrom(presence.getTo().toBareJID());
log.info("acceptSubscription: " + subscribedPresence.toXML());
router.route(subscribedPresence);
}
}
}
}
}
原理:
使用插件实现PacketInterceptor接口,拦截presence包,收到订阅包的同时,新建一个presence包,把 from 和 to 换一下,类型为subscribe,发出去(代替客户端同意好友请求)。
效果:
自动同意好友请求,当对方上线时,按同意按钮自动反加。双方订阅关系变为both。