我学过一年多的C#和两年多的java,脚本语言一直没怎么写过。因为项目需要,写了一个PHP的模拟登录程序,算是我在原公司做的最后一点贡献了。
程序写的很费劲,花了几天的时间,靠百度,google,zend studio和PHP Mysql的一本书撑下来了。
模拟登录不是真正意义上的单点登录,它又叫做伪单点登录,应用漫游,代填口令等等。
场景是这样的:用户访问一个应用,比如说新浪邮件,首次访问时,应用会提示输入新浪的用户名和密码。保存后,将用户名和密码填充到动态表单,使用动态生成的表单提交,从而实现自动登录。
如果用户修改了新浪里的密码,则提交会失败。这就有一个问题,需要先用指定的用户名和密码尝试登录,如果尝试登录成功,则post;如果不成功,则重定向到录入界面,直到提交成功。
写这个程序的关键点在两个地方,一个而是尝试登录程序,一个是动态表单生成程序。
关键问题找到了,首先我们考虑第一个问题。要模拟浏览器尝试登录,要有一个类似java里的Apache HttpClient的东西。我google很长时间,找到了一个php的HttpClient。开始我艰难的尝试之旅。
分析http请求相关信息,我用的是HttpWatch。
参考php HttpClient说明,很快我写了一段代码,用来尝试登录。尝试了n次,也没有成功。一开始我是直接post的,因为session id是通过cookie传递的,因为没有session信息,所以总是不成功,真土啊,对http协议理解不够深,相当于补课了。问了有此方面经验的同事,他说一般有两种,一种是使用HttpClient,一种是用httpwatch分析后,把cookie值抄下来,然后尝试登录。怎么想第二种方法都是太土了,但第一种方式又实现不了。
我又开始google,发现都是介绍java的httpclient的,php的几乎没有。但这次google有一个发现,就是apache的httpclient可以保存多次请求的信息,比如cookie,有记忆的。而我找的HttpClient只能单次请求,也就是说,每次都要手动设置cookie。我突然有想到,我何不从登录页面开始呢?这样我从登录页面开始get,然后将得到的cookie,再set一遍,然后开始post。
还是不行!
我开启了这个Http类的debug,发现post之后,重定向得到bad request,原来是重定向得到的path,没有“/”,这是原作者的一个bug。还一个问题比较大,就是他是重定向到另外的域,如果我还用之前设置的域,访问路径完全是错的。这样我在HttpClient里增加了两个参数,一个是判断是否重定向到其它域,如果重定向到其它域。把another_host赋给当前的host。
终于成功了!
我通过取回内容title中的值来判断是否登录成功,并把这些配置到数据库中。下面是我的数据库模型图:
动态表单生成就比较简单了,直接拼凑html:
<?php
require_once 'mysql.connect.php';
require_once 'AppBean.class.php';
/**
* 生成动态表单
* author 张永吉(gurudk)
*/
class DynamicForm {
var $appid;
var $loginUrl;
var $host;
var $cookieHost;
var $port;
var $formUserName;
var $formPassword;
var $errorInfo;
var $appProperties = array();
var $appBean;
var $loginUserValue;
var $loginPassValue;
public function __construct($appid) {
$this->appBean = new AppBean($appid);
$this->loginUrl = $this->appBean->loginUrl;
$this->host = $this->appBean->host;
$this->cookieHost = $this->appBean->cookieHost;
$this->port = $this->appBean->port;
$this->formUserName = $this->appBean->formUserName;
$this->formPassword = $this->appBean->formPassword;
$this->errorInfo = $this->appBean->errorInfo;
$this->appProperties = $this->appBean->appProperties;
}
public function __destruct(){
}
/**
* 生成html
* document.dynamicform.submit()
* @return unknown
*/
public function generate() {
$content = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">".
"<html>".
"<head>".
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">".
"<title>Insert title here</title>".
"</head>".
"<body onload='document.dynamicform.submit()'>".
"<form name=\"dynamicform\" method=\"POST\" action=\"".$this->loginUrl."\" >".
$this->generateMultibox().
$this->generateUserPasswordControl().
" <input type=\"submit\" name=\"btnlogin\" value=\"login\">".
"</form>".
"</body>".
"</html>";
return $content;
}
/**
* 生成下拉框或者radio组选框
*
* @return unknown
*/
private function generateMultibox(){
$i = 0;
$html = "";
while($key = key($this->appProperties)){
if("combox" == $this->appProperties[$key]["type"]){
$html = "<select id=\"freeselect\" name=\"".$this->appProperties[$key]["key"]."\">";
$values = split(",", $this->appProperties[$key]["value"]);
do{
//对于下拉框,value中可接受逗号间隔的列表,第一个值为选中值。
if($i == 0){
$html = $html."<option value=\"".current($values)."\" selected=\"selected\">".current($values)."</option>";
}else{
$html = $html."<option value=\"".current($values)."\">".current($values)."</option>";
}
$i++;
}while(next($values));
$html = $html."</select>";
}else if("hidden" == $this->appProperties[$key]["type"]){
$html .= "<input type='hidden' name=\"".$this->appProperties[$key]["key"]."\" value=\"".$this->appProperties[$key]["value"]."\">";
}
next($this->appProperties);
}
return $html;
}
/**
* 生成用户名和密码输入框
*
* @return unknown
*/
private function generateUserPasswordControl(){
$html = " <input type='hidden' name=\"".$this->formUserName."\" value=\"".$this->loginUserValue."\">".
" <input type=\"password\" name=\"".$this->formPassword."\" value=\"".$this->loginPassValue."\" />";
return $html;
}
/**
* 填充用户名和密码表单值
*
* @param unknown_type $userValue
* @param unknown_type $passValue
*/
public function setAutoLoginValue($userValue, $passValue){
$this->loginUserValue = $userValue;
$this->loginPassValue = $passValue;
}
}
?>
尝试登录的代码:
<?php
require_once 'HttpClient.php';
require_once 'AppBean.class.php';
/**
* 测试当前用户名和密码是否可以登录远程服务器
*
* author 张永吉(gurudk)
*/
class LoginTry {
var $appBean;
var $browser;
var $userName;
var $password;
/**
*
*/
function __construct($appid, $userName, $password) {
$this->appBean = new AppBean ( $appid );
$this->userName = $userName;
$this->password = $password;
$this->setUpBrowser();
}
/**
*
*/
function __destruct() {
//TODO - Insert your code here
}
private function setUpBrowser() {
$this->browser = new HttpClient ( $this->appBean->host, $this->appBean->port );
$client->max_redirects = 10;
$this->browser->cookie_host = $this->appBean->cookieHost;
$this->browser->setUserAgent ( 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)' );
}
/**
* 对指定网站进行尝试登录
*
*/
public function tryLogin() {
if($this->getTitle() == $this->appBean->errorInfo){
return false;
}
return true;
}
public function getTitle(){
$this->browser->get ( "/" );
$this->browser->setCookies ( $this->browser->getCookies () );
$this->browser->redirect_to_other_site = $this->appBean->isRedirectOther;
$this->browser->another_host = $this->appBean->anotherHost;
$this->browser->post ( $this->appBean->postUrl, $this->buildPostParams());
$this->browser->getContent ();
preg_match_all("/<title>(.*)<\/title>/i", $this->browser->getContent (), $matches);
$result = $matches[1][0];
//编码转换
if("utf-8" == $this->appBean->encode){
$title = iconv("utf-8", "gb2312", $result);
}else{
$title = $result;
}
return $title;
}
/**
* 读取数据库数据,构造post参数array
*
*/
public function buildPostParams(){
$params = array ($this->appBean->formUserName => $this->userName, $this->appBean->formPassword => $this->password );
while($key = key($this->appBean->appProperties)){
$this->copyOtherParams($params, $this->appBean->appProperties[$key]);
next($this->appBean->appProperties);
}
return $params;
}
/**
* copy除用户名,密码外其它需要配置的参数,在数据库应用属性表中进行配置
*
* @param unknown_type $params
* @param unknown_type $propArray
*/
private function copyOtherParams(&$params, $propArray){
$controlType = $propArray["type"];
if("combox" == $controlType || "radio" == $controlType){
$params[$propArray["key"]] = current(split(",", $propArray["value"]));
}else{
$params[$propArray["key"]] = $propArray["value"];
}
}
}
?>
用这种做法,登录新浪邮件是没问题的,但是登录网易时,我使用localhost是可以的,使用ip地址和其它域名不行。
可能网易邮件会验证refer地址,但验证refer地址的算法有点问题,:)。
分享到:
相关推荐
在本实例中,我们关注的是一个基于ASP的网站整站程序源码,它结合了PHP和AJAX技术来实现一个域名查询功能。这个功能允许用户在不刷新整个页面的情况下,实时检查特定域名的可用性,提高了用户体验。 首先,ASP源码...
这个实例源码是关于如何使用PHP编写一个程序,以实现自动在QQ空间的说说下面抢到第一条评论,即“抢一楼”的功能。在QQ空间,用户发布说说后,其他用户可以对其进行评论,而“抢一楼”通常意味着第一个留下评论的人...
2.2 建立第一个Android项目(HelloAndroid!) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 ...
【ASP论坛网站实例开发源码——Discuz论坛自动注册实例开发.zip】是一个包含ASP编程语言实现的论坛自动注册功能的源代码包。该实例适用于开发者或学习者了解和研究如何在Discuz论坛系统中实现自动化注册流程。在这个...
##### 第一步:\includes\cls_session.php的修改 在**cls_session.php**文件中,我们需要找到并修改购物车清理逻辑,确保只有匿名用户的购物车才会被清空。 1. **搜索原代码**:首先,搜索以下代码: ```php $...
本实例开发源码是基于PHP实现的,旨在模仿58团购网站的功能,特别强调了多城市支持和移动适配,对于初学者和开发者来说,这是一个非常有价值的参考资源。 1. **多城市支持**:在58团购程序中,多城市支持是一项关键...
实例003 第1个PHP程序 7 1.2 XAMPP——PHP集成化安装包 8 实例004 通过XAMPP配置PHP开发环境 8 实例005 测试XAMPP是否安装成功 11 实例006 XAMPP应用技巧 12 实例007 第2个PHP程序 13 1.3 IIS+PHP+MySQL——独立搭建...
实例028 通过Dreamweaver开发第1个PHP程序 48 1.7 Zend Studio开发工具 50 实例029 安装Zend Studio 50 实例030 Zend Studio创建PHP项目 52 实例031 Zend Studio编码格式的转换 56 实例032 Zend Studio中快捷键的...
实例028 通过Dreamweaver开发第1个PHP程序 48 1.7 Zend Studio开发工具 50 实例029 安装Zend Studio 50 实例030 Zend Studio创建PHP项目 52 实例031 Zend Studio编码格式的转换 56 实例032 Zend Studio中快捷键的...
在Thinkphp框架中,实现第三方登录的第一步是安装对应的OAuth库。例如,对于微信登录,我们需要安装微信官方提供的SDK,通过Composer管理依赖。接着,配置相应的AppID和AppSecret,这些信息可以在各社交平台的开发者...
【myschool学员管理系统】是一款专为教育机构设计的高效能管理工具,旨在优化学员和教师的管理工作流程。...这款系统是教育信息化进程中的一个重要工具,对于北大青鸟这样的专业教育机构来说,无疑是一大助力。
本实例开发源码——进云仿美团外卖源码,就是基于PHP构建的一个类似于美团外卖的在线订餐系统,旨在帮助开发者理解和学习PHP在实际项目中的应用。 首先,我们需要了解PHP的基本概念。PHP(Hypertext Preprocessor)...
在`index.php`首页文件的最顶端放置代码是必要的,因为这样可以在用户访问网站的第一时间启动防御机制。这通常涉及到在请求处理之前检查和过滤潜在的CC攻击。代码可能包含初始化防CC模块、设置阈值和规则,以及记录...
综上所述,这个基于PHP的FastMail EDM邮件到达率测试工具是为了解决邮件营销中的一个重要问题——如何准确测量邮件到达率。通过利用PHP和FastMail的特性,它为用户提供了评估和优化邮件发送策略的手段。
这个压缩包文件“PHP欢乐夹娃娃微信免签即时到账.zip”显然包含了一个基于PHP的在线游戏项目——欢乐夹娃娃,它集成了微信支付功能,并且实现了免签即时到账的特性,这对于那些希望快速部署游戏并避免复杂支付流程的...
在本文中,我们将深入探讨Laravel开发中的一个重要概念——JWT(JSON Web Tokens)以及与之相关的laravel-jwt-impersonate插件。Laravel是一个流行的PHP框架,它提供了丰富的功能来帮助开发者构建优雅的Web应用程序...
to 接收者的标志 (可以是手机号 也可以是昵称 ,昵称如果有重复的默认发第一个的)(注意:不能给自己发送) (不和v1兼容) msg 消息的正文(默认gbk 编码) (-v1) (可选) u 是否使用utf8编码(默认的编码是gbk , 此参数...