`

我的第一个PHP程序——实现网站的模拟登录

阅读更多

      我学过一年多的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 域名查询实例开发.zip

    在本实例中,我们关注的是一个基于ASP的网站整站程序源码,它结合了PHP和AJAX技术来实现一个域名查询功能。这个功能允许用户在不刷新整个页面的情况下,实时检查特定域名的可用性,提高了用户体验。 首先,ASP源码...

    PHP实例开发源码——QQ空间说说自动抢一楼源码.zip

    这个实例源码是关于如何使用PHP编写一个程序,以实现自动在QQ空间的说说下面抢到第一条评论,即“抢一楼”的功能。在QQ空间,用户发布说说后,其他用户可以对其进行评论,而“抢一楼”通常意味着第一个留下评论的人...

    Google Android SDK开发范例大全(完整版)

    2.2 建立第一个Android项目(HelloAndroid!) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 ...

    ASP论坛网站实例开发源码——Discuz论坛自动注册实例开发.zip

    【ASP论坛网站实例开发源码——Discuz论坛自动注册实例开发.zip】是一个包含ASP编程语言实现的论坛自动注册功能的源代码包。该实例适用于开发者或学习者了解和研究如何在Discuz论坛系统中实现自动化注册流程。在这个...

    PHP开发实战1200例(第1卷).(清华出版.潘凯华.刘中华).part2

    实例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中快捷键的...

    ECShop——会员退出不清空购物车

    ##### 第一步:\includes\cls_session.php的修改 在**cls_session.php**文件中,我们需要找到并修改购物车清理逻辑,确保只有匿名用户的购物车才会被清空。 1. **搜索原代码**:首先,搜索以下代码: ```php $...

    PHP实例开发源码-仿58团购程序多城市+手机版.zip

    本实例开发源码是基于PHP实现的,旨在模仿58团购网站的功能,特别强调了多城市支持和移动适配,对于初学者和开发者来说,这是一个非常有价值的参考资源。 1. **多城市支持**:在58团购程序中,多城市支持是一项关键...

    PHP开发实战1200例源码

    实例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——独立搭建...

    PHP开发实战1200例(第1卷).(清华出版.潘凯华.刘中华).part1

    实例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第三方登录演示

    在Thinkphp框架中,实现第三方登录的第一步是安装对应的OAuth库。例如,对于微信登录,我们需要安装微信官方提供的SDK,通过Composer管理依赖。接着,配置相应的AppID和AppSecret,这些信息可以在各社交平台的开发者...

    myschool学员管理系统,功能强大

    【myschool学员管理系统】是一款专为教育机构设计的高效能管理工具,旨在优化学员和教师的管理工作流程。...这款系统是教育信息化进程中的一个重要工具,对于北大青鸟这样的专业教育机构来说,无疑是一大助力。

    PHP实例开发源码-进云仿美团外卖源码.zip

    本实例开发源码——进云仿美团外卖源码,就是基于PHP构建的一个类似于美团外卖的在线订餐系统,旨在帮助开发者理解和学习PHP在实际项目中的应用。 首先,我们需要了解PHP的基本概念。PHP(Hypertext Preprocessor)...

    网站防被cc攻击系统PHP源码

    在`index.php`首页文件的最顶端放置代码是必要的,因为这样可以在用户访问网站的第一时间启动防御机制。这通常涉及到在请求处理之前检查和过滤潜在的CC攻击。代码可能包含初始化防CC模块、设置阈值和规则,以及记录...

    基于PHP的FastMail EDM邮件到达率精准测试工具 php版.zip

    综上所述,这个基于PHP的FastMail EDM邮件到达率测试工具是为了解决邮件营销中的一个重要问题——如何准确测量邮件到达率。通过利用PHP和FastMail的特性,它为用户提供了评估和优化邮件发送策略的手段。

    PHP欢乐夹娃娃微信免签即时到账.zip

    这个压缩包文件“PHP欢乐夹娃娃微信免签即时到账.zip”显然包含了一个基于PHP的在线游戏项目——欢乐夹娃娃,它集成了微信支付功能,并且实现了免签即时到账的特性,这对于那些希望快速部署游戏并避免复杂支付流程的...

    Laravel开发-laravel-jwt-impersonate

    在本文中,我们将深入探讨Laravel开发中的一个重要概念——JWT(JSON Web Tokens)以及与之相关的laravel-jwt-impersonate插件。Laravel是一个流行的PHP框架,它提供了丰富的功能来帮助开发者构建优雅的Web应用程序...

    php飞信 pafetion 开源

    to 接收者的标志 (可以是手机号 也可以是昵称 ,昵称如果有重复的默认发第一个的)(注意:不能给自己发送) (不和v1兼容) msg 消息的正文(默认gbk 编码) (-v1) (可选) u 是否使用utf8编码(默认的编码是gbk , 此参数...

Global site tag (gtag.js) - Google Analytics