最近因开发项目的需要,有一个需求,就是很多SNS网站都有的通过 Email地址 导入好友列表,不过这次要导入的不是Email 列表,而是QQ的好友列表。
实现方式:
通过google一搜,实现的方式大概有下面这篇文章提到的几种方法:
最后我选择了通过模拟登录QQ邮箱的方式来实现,该实现方式在海内网上的好友查找功能也可以看到。
与其他大部分邮箱不同的是,如果使用纯数字的QQ号登录的话,除了密码,还需要输入验证码。
看到海内上的QQ好友导入功能也是需要输入验证码的,而且验证码的样子和QQ邮箱的很像。由于这是需要在用户手动输入密码的情况下才能实现的功能,因此输入验证码的工作也可以让用户手动来完成。
验证码处理:
通过对 http://mail.qq.com/ 页面的分析, QQ邮箱的验证码方式实现原理其实是很简单,当需要一张验证码图片或看不清而需要换一张时,它都是向地址 http://ptlogin2.qq.com/getimage?aid=23000101 发出请求,(页面上该地址是通过js生成的,为了防止浏览器缓存,地址末尾还会带有随机一个随机数),而该链接不但返回一张图片,还在http头部带有设置cookie的一段header。这样当用户提交表单的时候,浏览器就会把该cookie发送回服务器,服务器通过比较 该cookie值和经过某种运算后的表单中的验证码值 就可以判断验证码是否填写正确。
现在的问题是由于cookie的安全机制,验证码图片不能直接从腾讯的服务器上去取,那样用户在将QQ和密码发送到我们的服务器时,验证码的cookie不会一起发过来。
解决方式其实也很简单,将验证码的获取地址改为我们自己的服务器,我们的服务器作为简单的代理,从腾讯的服务器上去获取真正的验证码,再将图片内容和那段cookie发送回用户浏览器。那样用户提交表单的时候,那段cookie就又会发送回我们的服务器了。
绕过其他验证安全机制:
一般上有了账号,密码,验证码这3样东西就可以实现模拟登录很多网站了,但是QQ邮箱还有其他的安全机制,在QQ邮箱登陆的表单中还有一个像这样 的 hidden 域,该value每次刷新页面都会改变,同时在表单提交的时候,还会通过js将该值与其他hidden 域的值进行某些计算才正式提交表单。
通过多次模拟登录,估计该值是用来判断登录session超时的,同时也参与其他的一些干扰加密的计算。而且该值与验证码是完全无关的,因此在显示我们表单时,只要先去抓取一下 http://mail.qq.com/ 页面,从里面提取出ts 值, 连同其他所有 hidden 域 和相关计算的js代码放入我们的表单中就可以了。
因此,实际上我们的表单只需要稍微修改一下 http://mail.qq.com/ 页面的内容就可以作为显示给用户的表单。主要包括以下几个方面,这里我使用的django,所以使用django的模板语法:
1、 改为
2、表单的action地址改为我们自己这里假设为 /friends/ 因此
改为
3、图片验证码地址,有两个地方要改:
document.write("");
改为
document.write("");
另外一个changeimg 函数内, 也将相应的地址改为我们自己的服务器即可。
改了这些,页面看上去和原来几乎一样,只是所有交互都改到了我们的服务器上,出于版权和页面统一的需要,在使用到自己的网站上时,可以使用自己设计的页面,只要表单的初始化和提交与原来一样就可以了,甚至也可以通过阅读js部