一、构思

  • 从Firefox浏览器拷贝cURL命令(初始页、提交、提交后)
  • 自动分析curl形成模拟登录代码
  • 默认参数:ssl/302/gzip

二、实现

接口

(一)根据curl信息执行并解析结果 public function execCurl($curlContent, $callbackBefore = false, $callbackAfter = false)

(二)解析curl信息 protected function _parseCurl($curlContent)

(三)执行curl请求 protected function _execCurl

(四)获取上一次存储cookie的文件 public function getLastCookieFile()

(五)设置上一次存储cookie的文件 protected function setLastCookieFile($cookieFile)

(六)登录成功后,锁定上一次存储cookie的文件,避免覆盖 public function lockLastCookieFile()

(七)解锁上一次存储cookie的文件 public function unlockLastCookieFile()

(八)登录成功, get 方式获取url信息 public function getUrl($url, $header = false)

(九)登录成功, post 方式获取url信息 public function postUrl($url, $postData = false, $header = false)

(十)记录日志 protected function _log($msg)

<?php

namespace PhpUtility;

/**
* class CurlAutoLogin
* @author Zjmainstay
* @website http://www.zjmainstay.cn
*
* 利用curl信息自动解析实现模拟登录
*/
class CurlAutoLogin {
//最后一次cookie存储文件
protected $lastCookieFile = '';
//登录成功后,锁定cookie的更新
protected $lockedLastCookieFile = false; /**
* 根据curl信息执行并解析结果
* @param string $curlContent 利用Firefox浏览器复制cURL命令
* @param boolean $callbackBefore 对curl结果前置处理,如更换用户名、密码等
* @param boolean $callbackAfter 对采集结果后置处理,如解析结果的csrf token等
* @return mixed
*/
public function execCurl($curlContent, $callbackBefore = false, $callbackAfter = false) {
$parseCurlResult = $this->_parseCurl($curlContent);
if(!empty($callbackBefore)) {
$parseCurlResult = $callbackBefore($parseCurlResult);
}
$execCurlResult = $this->_execCurl($parseCurlResult); if(!empty($callbackAfter)) {
$execCurlResult = $callbackAfter($parseCurlResult, $execCurlResult);
} return $execCurlResult;
} /**
* 解析curl信息
* @param string $curlContent 利用Firefox浏览器复制cURL命令
* @return bool|array
*/
protected function _parseCurl($curlContent) {
if(!preg_match("#curl '([^']*?)'#is", $curlContent, $matchUrl)) {
return false;
} //remove cookie data in header
$curlContent = preg_replace("#-H 'Cookie:[^']*'#is", '', $curlContent); if(!preg_match_all("#-H '([^']*?)'#is", $curlContent, $headerMatches)) {
$httpHeader = [];
} else {
$httpHeader = $headerMatches[1];
} if(!preg_match("#--data '([^']*?)'#is", $curlContent, $postDataMatch)) {
$postData = '';
} else {
$postData = $postDataMatch[1];
} return [
'url' => $matchUrl[1],
'header' => $httpHeader,
'post' => $postData,
];
} /**
* 执行curl请求
* @param array $parseCurlResult curl信息的解析结果,包含 url/header/post 三个键值参数
* @return string
*/
protected function _execCurl($parseCurlResult) {
if(empty($parseCurlResult['url'])) {
return '';
} $ch = curl_init($parseCurlResult['url']);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip压缩 //add header
if(!empty($parseCurlResult['header'])) {
$this->curl->opt[CURLOPT_HTTPHEADER] = $parseCurlResult['header'];
} //add ssl support
if(substr($parseCurlResult['url'], 0, 5) == 'https') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 报错时使用
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 报错时使用
} //add 302 support
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //add cookie support
//设置一个不存在的目录以在系统临时目录随机生成一个缓存文件,避免多进程cookie覆盖
$cookieFile = tempnam('/not_exist_dir/', 'autologin');
curl_setopt($ch,CURLOPT_COOKIEJAR,$cookieFile); //存储提交后得到的cookie数据 //add previous curl cookie
if(!empty($this->lastCookieFile)) {
curl_setopt($ch,CURLOPT_COOKIEFILE, $this->lastCookieFile); //使用提交后得到的cookie数据
} //add post data support
if(!empty($parseCurlResult['post'])) {
curl_setopt($ch,CURLOPT_POST, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS, $parseCurlResult['post']);
} try {
$content = curl_exec($ch); //执行并存储结果
} catch (\Exception $e) {
$this->_log($e->getMessage());
} $curlError = curl_error($ch);
if(!empty($curlError)) {
$this->_log($curlError);
} curl_close($ch); //update last cookie file
$this->setLastCookieFile($cookieFile); return $content;
} /**
* 记录日志
* @param [type] $msg [description]
* @return [type] [description]
*/
protected function _log($msg) {
file_put_contents(__DIR__ . '/run.log', $msg . "\n", 8);
} /**
* 获取上一次存储cookie的文件
* @return [type] [description]
*/
public function getLastCookieFile() {
return $this->lastCookieFile;
} /**
* 设置上一次存储cookie的文件
* @param [type] $cookieFile [description]
*/
protected function setLastCookieFile($cookieFile) {
if(!$this->lockedLastCookieFile) {
$this->lastCookieFile = $cookieFile;
}
} /**
* 登录成功后,锁定上一次存储cookie的文件,避免覆盖
* @return [type] [description]
*/
public function lockLastCookieFile() {
$this->lockedLastCookieFile = true;
} /**
* 解锁上一次存储cookie的文件
* @return [type] [description]
*/
public function unlockLastCookieFile() {
$this->lockedLastCookieFile = false;
} /**
* 登录成功, get 方式获取url信息
* @param [type] $url [description]
* @param boolean $header [description]
* @return [type] [description]
*/
public function getUrl($url, $header = false) {
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip压缩 //add header
if(!empty($header)) {
$this->curl->opt[CURLOPT_HTTPHEADER] = $header;
} //add ssl support
if(substr($url, 0, 5) == 'https') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 报错时使用
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 报错时使用
} //add 302 support
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch,CURLOPT_COOKIEFILE, $this->lastCookieFile); //使用提交后得到的cookie数据 try {
$content = curl_exec($ch); //执行并存储结果
} catch (\Exception $e) {
$this->_log($e->getMessage());
} $curlError = curl_error($ch);
if(!empty($curlError)) {
$this->_log($curlError);
} curl_close($ch); return $content;
} /**
* 登录成功, post 方式获取url信息
* @param [type] $url [description]
* @param boolean $postData [description]
* @param boolean $header [description]
* @return [type] [description]
*/
public function postUrl($url, $postData = false, $header = false) {
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip压缩 //add header
if(!empty($header)) {
$this->curl->opt[CURLOPT_HTTPHEADER] = $header;
} //add ssl support
if(substr($url, 0, 5) == 'https') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 报错时使用
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 报错时使用
} //add 302 support
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch,CURLOPT_COOKIEFILE, $this->lastCookieFile); //使用提交后得到的cookie数据 //add post data support
if(!empty($postData)) {
curl_setopt($ch,CURLOPT_POST, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS, $postData);
} try {
$content = curl_exec($ch); //执行并存储结果
} catch (\Exception $e) {
$this->_log($e->getMessage());
} $curlError = curl_error($ch);
if(!empty($curlError)) {
$this->_log($curlError);
} curl_close($ch); return $content;
}
}

三、演示

运行:PHP cURL自动模拟登录演示

<?php

require_once __DIR__.'/../vendor/autoload.php';

$autologin = new PhpUtility\CurlAutoLogin();

//0. 未登录
$getDataUrl = 'http://demo.zjmainstay.cn/js/simpleAjax/loginResult.php';
echo 'Before Login: ' . $autologin->getUrl($getDataUrl) . "\n"; //1. 初始化登录页
$firstCurl = "curl 'http://demo.zjmainstay.cn/js/simpleAjax/' -H 'Host: demo.zjmainstay.cn' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:47.0) Gecko/20100101 Firefox/47.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Accept-Encoding: gzip, deflate' -H 'Cookie: Hm_lvt_1526d5aecf5561ef9401f7c7b7842a97=1468327822,1468327904,1468341636,1468411918; Hm_lpvt_1526d5aecf5561ef9401f7c7b7842a97=1468421526' -H 'Connection: keep-alive' -H 'If-Modified-Since: Mon, 27 Oct 2014 08:31:18 GMT' -H 'If-None-Match: \"32e-453-506635ac5e180\"' -H 'Cache-Control: max-age=0'";
$autologin->execCurl($firstCurl); //2. 提交登录表单
$secondCurl = "curl 'http://demo.zjmainstay.cn/js/simpleAjax/doPost.php' -H 'Host: demo.zjmainstay.cn' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:47.0) Gecko/20100101 Firefox/47.0' -H 'Accept: application/json, text/javascript, */*; q=0.01' -H 'Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Accept-Encoding: gzip, deflate' -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-Requested-With: XMLHttpRequest' -H 'Referer: http://demo.zjmainstay.cn/js/simpleAjax/' -H 'Cookie: Hm_lvt_1526d5aecf5561ef9401f7c7b7842a97=1468327822,1468327904,1468341636,1468411918; Hm_lpvt_1526d5aecf5561ef9401f7c7b7842a97=1468421526' -H 'Connection: keep-alive' --data 'username=demousername'";
$realUsername = 'Zjmainstay';
//前置处理,替换错误的用户名
$autologin->execCurl($secondCurl, function($parseCurlResult) use ($realUsername) {
$parseCurlResult['post'] = str_replace('=demousername', "={$realUsername}", $parseCurlResult['post']);
return $parseCurlResult;
}); //3. 登录成功,锁定cookie的更新,直接访问已登录页面内容
$autologin->lockLastCookieFile();
echo 'After Login: ' . $autologin->getUrl($getDataUrl) . "\n";

四、更多

请关注github项目 php-utility-class 上面的更新。

文章首发自Zjmainstay学习笔记《PHP基于cURL实现自动模拟登录

模拟登录神器之PHP基于cURL实现自动模拟登录类的更多相关文章

  1. PHP cURL应用实现模拟登录与采集使用方法详解

    对于做过数据采集的人来说,cURL一定不会陌生.虽然在PHP中有file_get_contents函数可以获取远程链接的数据,但是它的可控制性太差了,对于各种复杂情况的采集情景,file_get_co ...

  2. .NET基于Redis缓存实现单点登录SSO的解决方案[转]

    一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单点登录,现在就NET基于Redis缓存实现单点登录做一个简单的分享. 单点登录(Single Sign On),简称 ...

  3. .NET基于Redis缓存实现单点登录SSO的解决方案

    一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单点登录,现在就NET基于Redis缓存实现单点登录做一个简单的分享. 单点登录(Single Sign On),简称 ...

  4. ORACLE恢复神器之ODU/AUL/DUL

    分享ORACLE数据库恢复神器之ODU.DUL和AUL工具. ODU:ORACLE DATABASE UNLOADER DUL:DATA UNLOADER AUL:也称MyDUL 关于三种工具说明: ...

  5. CURL PHP模拟浏览器get和post

    模拟浏览器get和post数据需要经常用到的类, 在这里收藏了几个不错的方法 方法一 <?php define ( 'IS_PROXY', true ); //是否启用代理 /* cookie文 ...

  6. SSH客户端神器之 MobaXterm

    SSH客户端神器之 MobaXterm 由于需要连接远程 Linux 服务器,早期使用过 Putty,SecureCRT,后面主要使用 Xshell. 自从接触了 MobaXterm之后,个人感觉比 ...

  7. 网站实现微信登录之回调函数中登录逻辑的处理--基于yii2开发的描述

    上一篇文章网站实现微信登录之嵌入二维码中描述了如何在自己的登录页面内嵌入登录二维码,今天的这篇文章主要是描述下在扫码成功之后微信重定向回网站后登录逻辑的处理,其实也就是验证身份信息,授权用户登录的逻辑 ...

  8. 代码轮子之很简单但是挺管用的基于C# Task的模拟并发的代码

    代码轮子之很简单但是挺管用的基于C# Task的模拟并发的代码

  9. SSO 基于Cookie+fliter实现单点登录(SSO):工作原理

    SSO的概念: 单点登录SSO(Single Sign-On)是身份管理中的一部分. SSO的一种较为通俗的定义是:SSO是指訪问同一server不同应用中的受保护资源的同一用户,仅仅须要登录一次,即 ...

随机推荐

  1. vim安装YouCompleteMe 插件

    要安装YouCompleteMe ,vim须支持python.看是否支持,可以在vim中:version 查看, 如果python前有+号,就是支持,减号就是不支持. 如果不支持,需要以编译安装方式重 ...

  2. HTML Meta标签知多少

    文章已同步至个人Blog:Benjamin - 专注前端开发和用户体验 一.基本属性 标签常常被用来定义HTML文档的元数据或者HTTP协议的指向,这些元数据常用在SEO.HTML Pages or ...

  3. 使用Jekyll官方的ReadMore摘要功能

    今天才发现,Jekyll官方就支持ReadMore摘要功能,记录一下. 我之前的方法,在index.html中 {{ post.content ||split:'<!-- more --> ...

  4. Ubuntu 安装配置MySQL,并使用VS的Server Explorer UI界面远程管理MySQL

    为安装配置方便,使用root账号登入Ubuntu. step1: 键入下面命令安装MySQL. 过程十分简单.安装过程中只需根据提示输入root账号的密码即可. step2:安装完成后检查MySQL是 ...

  5. JavaScript原生DOM操作API总结

    最近面试的时候被这个问题给卡了,所以抽时间好好复习一下. 原文:http://www.cnblogs.com/liuxianan/p/javascript-dom-api.html 几种对象 Node ...

  6. 关于网卡eth0、eth1以及服务器为什么要把内网和外网卡区分开

    在搜搜上看到了这个回答,它解释了什么是eth0,eth1: eth0和eth1这是网卡设备,只是个名称不必纠结.通常服务器会有多个网卡的,所以就有eth0 eth1 eth2 这样的名称,而且在一些系 ...

  7. podspec文件介绍

    podspec文件是cocopods引入的第三方代码库的配置索引文件 它的创建命令是:$pod spec create TestFile podspec文件的常用配置字段介绍 Pod::Spec.ne ...

  8. iOS开发----地图与导航--定位和位置信息获取

    要实现地图.导航功能,往往需要先熟悉定位功能,在iOS中通过Core Location框架进行定位操作.Core Location自身可以单独使用,和地图开发框架MapKit完全是独立的,但是往往地图 ...

  9. Leetcode 168 Excel Sheet Column Title 进制数转化

    题意:将数字转化成excel表中的行中的项目 本质是10进制转化为26进制,但是在中间加入了一个不一样的操作,在每次操作前都需要n-- class Solution { public: string ...

  10. 强烈推荐android studio用的几个插件

    http://blog.csdn.net/liang5630/article/details/46366901 android studio常用插件,可极大简化开发,增强开发效率. 不懂安装studi ...