分享

[XMPP]我是怎么通过直接操作数据来为Openfire注册新用户的

 WindySky 2016-02-25

众所周知,Openfire的注册方式一般有三种:

1.带内注册  ---- In-Band Registration. 即客户端通过匿名方式与Openfire 服务器端建立连接并验证,然后发起注册节点XML流,以XMPPStream的方法直接像服务器注册。code4app上有另外一个XMPP的带内注册demo详细的示范了这个方法(不是我写的)。

优点:不需假设额外的服务器端,适合对java一窍不通的开发者。

弊端:不容易管理用户账号。用户注册的JID无规律,不能够自增长。安全性低。

2.Openfire二次开发(为Openfire写插件)  ----- 这种方式是通过下载Openfire(JiveSoftware开放Openfire开源代码)的方式替代直接安装Openfire安装包来进行二次开发。二次开发非常强大,我后面会提到用这种方式获取用户的分组、在线信息、群体推送消息等都非常的方便。但是工作量相对较大,不建议java新手选择。

3.外接Openfire数据库至本机的mySQL 数据库中,直接操作ofUser表并对其加密方式和字段规则进行模仿,可以达到与后台添加账号完全相同的功效。我的仿微信Demo即是采取的这种方式。

优点:可以使用比JDBC更好用的hibernate来操作openfire中的自带用户表来享受hibernate的性能。生成的jid中的用户名可以自增长的方式方便日后管理。

缺点:不同的电脑,或者重装Openfire都会让加密钥随之改变,所以如果遇到此情况需要将原有密文密码以旧秘钥解密后再使用新秘钥加密。


OK,本博客主要讨论的就是方法3了,在讨论此方法之前,读者首先要能够赞同这种方式,其次,您需要在openfire安装的时候选择外接数据库,并导入到mysql中。


确定openfire数据库外接至mysql成功并且配置完成后,我们就可以开始注册行动了。


1.找到openfire默认数据库中的用户表 ofUser






呵呵,这就是我的demo所使用的数据库了,383以前的用户被我删掉了,现在已经有1143个人注册了我的demo,所以你们的用户名与密码都在这里~ 注意,这里的plainPassword一般不会使用,openfire的用户名密码一般都是用后面的encryptedPassword来存储的,这里我保留了部分明文明码也是为了方便万一大家忘了密码我可以直接告诉他,因为这么多用户我也疏于管理,免费的开源事业不可能还要有客服帮你找回密码对吧。。。


仔细分析这个表的各个Colunm结构之后(如果你是新安装的,你观察amdin这个用户的信息就行了),我们发现如果我们要新建一个用户,很多字段都是可空的,但是为了尽量不影响openfire本身,我决定伪造时间戳与密文密码,然后自增长username来达到注册目的。你这里可以不使用自增长username。


既然我们要模仿openfire的原生行为来炮制一场虚拟的注册,那么我先分析下这个表的各个column的类型:



注意 虽然只有3个非空字段,但是实质上你至少要有4个字段,即这3个字段加上 明文 or 密文密码,不然你没有密码怎么登陆。其次,注意看这里的CHAR(15)类型的时间戳,我们知道ios的时间戳是以秒为个位数的浮点数,java的时间戳是以毫秒为单位的13位的整数(至笔者写此博客为止是13位,不排除突破14位的可能,估计要等几百年吧),不难发现这个CHAR其实就是java时间戳的CHAR[]格式数据,这个就非常好办了。userName我们让他自增长,也好办。接下来就是密码了,这里大家最好不要使用明文密码,不然以后登陆出现问题,我不负责。

OK不啰嗦了,简单说下密文密码怎样伪造。首先我们需要研究openfire的加密原理,即他的加密方式。openfire使用其源码中的BlowFish.java 工具类来进行加密,简单的看看他的源码不难发现其加密方式实质为digest:


所以我们要成功伪造一个合格的密码,必须拿到这个文件并且调用他。大家可以到www.:8080下载页面java源码中找到他。


这个加密类的工作原理很简单,以每个openfire安装出来后生成的passwordKey作为key,再以时间戳的某种形式作为变量拼接上用户的明文密码一起混合加密。其结果是同一密码不同openfire上加密后的结果不一样,同一密文密码解密后也不一样。即便是同一个server,不同时间加密后的密文密码也不一样,但是解密后的明文密码一样(因为时间戳和明文密码混合在一起加密,时间在变化)。

所以只要passwordKey不变,那么我们的对任意的明文密码在任意时候 进行加密,随时都可以解密回原明文密码。


Let's move,find the passwordKey!

这个秘钥大家可以在openfire控制台---服务器属性里找到,也可以在ofProperty表中找到他:


复制其value,然后我们可以创建一个servlet了(一个带void main的java文件也可以),将其粘贴至源码中并将其作为Blowfish实例的初始化方法的参数:

  1. static Blowfish _encoder=new Blowfish("4MMl61yXu1r16n6");  

然后我们就可以在任何地方进行加密了,例如:

  1. String oStr="admin";  
  2. String encodedStr=_encoder.encryptString(oStr);  

那么如果你忘记了你的admin密码进不去管理中心,这个方法也同样可以轻松帮你搞定,你只需要将这个password输出并且复制到数据库中粘贴到encryptedPassword中,然后重启openfire即可。



加密到这里就OK了,解密的话只是需要调用deencryptString方法即可。同样也可以帮你找回密码,你只需要去数据库中找到加密后的密码然后解密输出,就可以直接登陆了。


接下来我们只需要拼凑出2个非空的时间戳即可。为了节约篇幅,我这里直接给出注册Openfire的私有方法代码:

  1. public boolean registerOpenFireUser(int userName,String userPassword) {  
  2.             String encodedStr=_encoder.encryptString(userPassword);  
  3.             try {  
  4.                 OfUser openFireUser=new OfUser(String.valueOf(userName), userPassword, encodedStr, "", "", String.format("00%d", System.currentTimeMillis()), String.format("00%d", System.currentTimeMillis()));  
  5.                 OfUserDAO ofDAO=new OfUserDAO();  
  6.                 ofDAO.save(openFireUser);  
  7.                   
  8.                 return true;  
  9.             } catch (Exception e) {  
  10.                 // TODO: handle exception  
  11.                 e.printStackTrace();  
  12.                 return false;  
  13.             }  
  14.               
  15.         }  

这里的00%d能这样写就是因为时间戳是13位的,补上2个0即可与系统生成的一致,伪装的毫无痕迹。

这里的Dao类我是用hibernate反向生成的,读者用jdbc直接插入一行ofUser也可以。

至此我们只需要在servlet中获取到用户的userName  userPassword,然后执行这个方法就可以了。

  1. registerOpenFireUser(thisUser.getUserId(), userPassword);  

OK,如果你是一个有经验的java程序员,我相信你已经写了一个非常好的servlet来替你注册了!就像我这个API地址一样,里面包含了用户头像,昵称,等等。http://www.:8080/user/register.html




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多