[英]cURL for login . White page or blank page
这是我尝试用于连接CFE网站的代码。 有人可以帮我弄这个吗。 我找不到代码有什么问题。
即使我更改了代码,也得到了(对象移至此处)
$ckfile = tempnam ("/tmp", "CURLCOOKIE");
$url =
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
$fields = array(
'ctl00$PHContenidoPag$UCLogin2$LoginUsuario$UserName' => 'xxxxxxxxxxxx',
'ctl00$PHContenidoPag$UCLogin2$LoginUsuario$Password' => 'xxxxxxxxxxxx',
);
$fields_string = '';
foreach($fields as $key=>$value) {
$fields_string .= $key . '=' . $value . '&';
}
rtrim($fields_string, '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$output = curl_exec($ch);
$url =
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
echo $output;
你做错了几件事。 首先,您的$ fields_string编码错误。 在application/x-www-urlencoded
,当您尝试编码时, $
必须被编码为%24
,但是您只需直接发送$即可。 但是如果您停止考虑的话,很明显您的编码方法是错误的,因为如果用户名或密码包含&
,您会怎么办? 如果用户名或密码中有&
,则必须将其编码为%26
,空格必须编码为%20
,依此类推,使用urlencode()对其进行编码,更正编码循环,如下所示:
foreach($fields as $key=>$value) {
$fields_string .= urlencode($key) . '=' . urlencode($value) . '&';
}
rtrim($fields_string, '&');
但幸运的是,php有一个专用于对application/x-www-urlencoded
进行编码的函数,称为http_build_query
,整个循环和修整可以(并且应该)用以下这一行代替:
$fields_string=http_build_query($fields);
其次,您制作一个curl句柄,设置CURLOPT_COOKIEJAR,并获取登录页面,我想您这样做是为了获得您的登录请求的cookie会话,您确实需要这样做,但是您没有关闭第一个curl句柄在创建全新的curl句柄以执行登录请求之前。 CURLOPT_COOKIEJAR在卷曲柄关闭时首先被刷新,这意味着您的第一个卷曲柄尚未保存cookie,因为您没有做curl_close,因此第二个卷曲柄无法加载第一个柄的cookie,这意味着它将尝试在没有cookie会话的情况下登录,需要在此处登录。
第三,您的代码完全忽略任何setopt错误。 如果设置您的选项时出现问题,curl_setopt返回bool(false),则不应忽略该问题。 为确保设置卷曲选项没有问题,我建议您改用以下功能:
function ecurl_setopt ( /*resource*/$ch , int $option , /*mixed*/ $value ):bool{
$ret=curl_setopt($ch,$option,$value);
if($ret!==true){
//option should be obvious by stack trace
throw new RuntimeException ( 'curl_setopt() failed. curl_errno: ' . curl_errno ($ch) .' curl_error: '.curl_error($ch) );
}
return true;
}
第四,此页面似乎采用了类似CSRF令牌的方案__VIEWSTATE和__EVENTVALIDATION,这在登录页面加载的html中给出,这在登录时是必需的,您的代码将完全忽略它们,您必须将它们从html中解析出来并将它们添加到您的登录请求中。 我强烈建议为此使用DOMDocument / DOMXPath(...但是最常见(也是有缺陷的)方法是正则表达式...)
第五,这行是荒谬的,并且会错误地起作用: curl_setopt($ch, CURLOPT_POST, count($fields));
它应该是布尔值true,而不是发布字段的数量(幸运的是,它仍然有效,因为任何大于零的int都是true-ish
,也足够接近,但是它仍然很奇怪,建议作者不知道自己在做什么)
最后,protip,您可以根据需要多次重复使用相同的curl会话,没有理由在此php代码中创建2个curl会话。 同样,在调试curl代码时,启用CURLOPT_VERBOSE,它会打印许多有用的调试信息。
这是一个示例代码,使用hhb_curl作为curl包装器(负责错误检测和报告,cookie处理等),不做任何错误,我认为可以在第3行和第4行使用正确的用户名和密码进行操作:
<?php
declare(strict_types = 1);
const USERNAME = '???';
const PASSWORD = '???';
header ( "content-type: text/plain;charset=utf8" );
require_once ('hhb_.inc.php');
$hc = new hhb_curl ( '', true );
$html = $hc->exec ( 'https://app.cfe.gob.mx/Aplicaciones/CCFE/Recibos/Consulta/login.aspx' )->getStdOut ();
$domd = @DOMDocument::loadHTML ( $html );
$inputsRaw = getDOMDocumentFormInputs ( $domd, true ) ['aspnetForm'];
$inputs = array ();
foreach ( $inputsRaw as $tmp ) {
$inputs [$tmp->getAttribute ( "name" )] = $tmp->getAttribute ( "value" );
}
assert ( isset ( $inputs ['__VIEWSTATE'], $inputs ['__EVENTVALIDATION'] ) );
$inputs ['ctl00$PHContenidoPag$UCLogin2$LoginUsuario$UserName'] = USERNAME;
$inputs ['ctl00$PHContenidoPag$UCLogin2$LoginUsuario$Password'] = PASSWORD;
hhb_var_dump ( $inputs );
$html = $hc->setopt_array ( array (
CURLOPT_URL => 'https://app.cfe.gob.mx/Aplicaciones/CCFE/Recibos/Consulta/login.aspx',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query ( $inputs )
) )->exec ()->getStdOut ();
// hhb_var_dump($html) & die();
$domd = @DOMDocument::loadHTML ( $html );
$xp = new DOMXPath ( $domd );
$loginErrors = $xp->query ( '//*[(contains(@style,"color:Red") or contains(@color,"Red")) and not(contains(@style,"hidden"))]' );
foreach ( $loginErrors as $tmp ) {
echo "login error!! ";
var_dump ( $tmp->textContent );
}
if (0 === $loginErrors->length) {
echo "login success!";
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false): array {
// :DOMNodeList?
$forms = $domd->getElementsByTagName ( 'form' );
$parsedForms = array ();
$isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
$parent = $decendant;
while ( NULL !== ($parent = $parent->parentNode) ) {
if ($parent === $ele) {
return true;
}
}
return false;
};
// i can't use array_merge on DOMNodeLists :(
$merged = function () use (&$domd): array {
$ret = array ();
foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
$ret [] = $input;
}
foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
$ret [] = $textarea;
}
foreach ( $domd->getElementsByTagName ( "button" ) as $button ) {
$ret [] = $button;
}
return $ret;
};
$merged = $merged ();
foreach ( $forms as $form ) {
$inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
$ret = array ();
foreach ( $merged as $input ) {
// hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
if ($input->hasAttribute ( "disabled" )) {
// ignore disabled elements?
continue;
}
$name = $input->getAttribute ( "name" );
if ($name === '') {
// echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
continue;
}
if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
// echo "this input does not belong to this form.", PHP_EOL;
continue;
}
if (! array_key_exists ( $name, $ret )) {
$ret [$name] = array (
$input
);
} else {
$ret [$name] [] = $input;
}
}
return $ret;
};
$inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
$hasName = true;
$name = $form->getAttribute ( "id" );
if ($name === '') {
$name = $form->getAttribute ( "name" );
if ($name === '') {
$hasName = false;
}
}
if (! $hasName) {
$parsedForms [] = array (
$inputs
);
} else {
if (! array_key_exists ( $name, $parsedForms )) {
$parsedForms [$name] = array (
$inputs
);
} else {
$parsedForms [$name] [] = $tmp;
}
}
}
unset ( $form, $tmp, $hasName, $name, $i, $input );
if ($getOnlyFirstMatches) {
foreach ( $parsedForms as $key => $val ) {
$parsedForms [$key] = $val [0];
}
unset ( $key, $val );
foreach ( $parsedForms as $key1 => $val1 ) {
foreach ( $val1 as $key2 => $val2 ) {
$parsedForms [$key1] [$key2] = $val2 [0];
}
}
}
return $parsedForms;
}
它当前输出:
login error!! string(35) "Usuario No Existente en Aplicacion."
说那个???
不是有效的用户名。
这是我遵循您的建议时得到的。 至少我没有把“对象移到这里”。 我可以获取登录页面,仅此而已。 它没有登录。尝试代码,至少您会看到它的作用。 谢谢大家的帮助。
<?php
$ckfile = tempnam ("/tmp", "CURLCOOKIE");
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
$fields = array(
'ctl00$PHContenidoPag$UCLogin2$LoginUsuario$UserName' => 'xxx',
'ctl00$PHContenidoPag$UCLogin2$LoginUsuario$Password' => 'xxx',
);
$fields_string = http_build_query($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key . '=' . $value . '&';
}
rtrim($fields_string, '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, count(1));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
$url =
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$output = curl_exec($ch);
echo $output;
?>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.