PHP 卷曲和 Cookie

Posted

技术标签:

【中文标题】PHP 卷曲和 Cookie【英文标题】:PHP Curl And Cookies 【发布时间】:2012-10-04 19:57:32 【问题描述】:

我在 php Curl 和 cookie 身份验证方面遇到了一些问题。

我有一个文件Connector.php,它对另一台服务器上的用户进行身份验证并返回当前用户的cookie。

问题是我想使用 curl 对数千个用户进行身份验证,但它一次只为一个用户进行身份验证和保存 COOKIES。

connector.php 的代码是这样的:

    <?php
    if(!count($_REQUEST)) 
        die("No Access!");
    


    //Core Url For Services
    define ('ServiceCore', 'http://example.com/core/');


    //Which Internal Service Should Be Called
    $path = $_GET['service'];


    //Service To Be Queried
    $url = ServiceCore.$path;

    //Open the Curl session
    $session = curl_init($url);

    // If it's a GET, put the GET data in the body
    if ($_GET['service']) 
        //Iterate Over GET Vars
        $postvars = '';
        foreach($_GET as $key=>$val) 
            if($key!='service') 
                $postvars.="$key=$val&";
            
        
        curl_setopt ($session, CURLOPT_POST, true);
        curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
    


    //Create And Save Cookies
    $tmpfname = dirname(__FILE__).'/cookie.txt';
    curl_setopt($session, CURLOPT_COOKIEJAR, $tmpfname);
    curl_setopt($session, CURLOPT_COOKIEFILE, $tmpfname);

    curl_setopt($session, CURLOPT_HEADER, false);
    curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($session, CURLOPT_FOLLOWLOCATION, true);

    // EXECUTE
    $json = curl_exec($session);
        echo $json;
    curl_close($session);
?>

认证过程如下:

    用户输入用户名和密码:Connector.php?service=logon&amp;user_name=user32&amp;user_pass=123 Connector.php?service=logosessionInfo 根据之前使用登录服务保存的 cookie 返回有关用户的信息。

问题是这段代码为每个用户将cookie保存在一个文件中,无法处理多个用户身份验证。

【问题讨论】:

我通过为具有唯一 PHPSESSID 的不同用户创建不同的 cookie 文件解决了我的问题。 $tmpfname = dirname(__FILE__).'/'.$_COOKIE['PHPSESSID'].'.txt'; 如果有很多用户是个好主意吗? 否 如果您有很多用户,这很糟糕。它会导致 apache 服务器崩溃。我用 apache 代理解决了这个问题。并删除了我所有的 CURL 代码。 【参考方案1】:

首先使用 tempnam() 函数创建临时 cookie:

$ckfile = tempnam ("/tmp", "CURLCOOKIE");

然后执行 curl init 将 cookie 保存为临时文件:

$ch = curl_init ("http://uri.com/");
curl_setopt ($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec ($ch);

或者使用存储在临时文件中的cookie访问页面:

$ch = curl_init ("http://somedomain.com/cookiepage.php");
curl_setopt ($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec ($ch);

这将初始化页面的cookie:

curl_setopt ($ch, CURLOPT_COOKIEFILE, $ckfile);

【讨论】:

如果有大量用户尝试同时访问该页面,而您已经在 /tmp 中有一些文件,该怎么办? PHP 文档说: 注意:如果 PHP 无法在指定的 dir 参数中创建文件,它将回退到系统默认值。在 NTFS 上,如果指定的目录包含超过 65534 个文件,也会发生这种情况。 如果存储时间超过 24 小时,请删除 cookie 文件。为 $ckfile 单独存储一个 cookie 数据,以检查 /tmp 中的所有文件。 3 年后,你仍然是像我这样的非 PHP 人士的救命稻草。在浏览了一堆 *** 答案后,这里有一个很好的解释。【参考方案2】:

在处理类似问题时,我在结合了我在网络上遇到的大量资源并添加了我自己的 cookie 处理后创建了以下函数。希望这对其他人有用。

      function get_web_page( $url, $cookiesIn = '' )
        $options = array(
            CURLOPT_RETURNTRANSFER => true,     // return web page
            CURLOPT_HEADER         => true,     //return headers in addition to content
            CURLOPT_FOLLOWLOCATION => true,     // follow redirects
            CURLOPT_ENCODING       => "",       // handle all encodings
            CURLOPT_AUTOREFERER    => true,     // set referer on redirect
            CURLOPT_CONNECTTIMEOUT => 120,      // timeout on connect
            CURLOPT_TIMEOUT        => 120,      // timeout on response
            CURLOPT_MAXREDIRS      => 10,       // stop after 10 redirects
            CURLINFO_HEADER_OUT    => true,
            CURLOPT_SSL_VERIFYPEER => true,     // Validate SSL Certificates
            CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_1_1,
            CURLOPT_COOKIE         => $cookiesIn
        );

        $ch      = curl_init( $url );
        curl_setopt_array( $ch, $options );
        $rough_content = curl_exec( $ch );
        $err     = curl_errno( $ch );
        $errmsg  = curl_error( $ch );
        $header  = curl_getinfo( $ch );
        curl_close( $ch );

        $header_content = substr($rough_content, 0, $header['header_size']);
        $body_content = trim(str_replace($header_content, '', $rough_content));
        $pattern = "#Set-Cookie:\\s+(?<cookie>[^=]+=[^;]+)#m"; 
        preg_match_all($pattern, $header_content, $matches); 
        $cookiesOut = implode("; ", $matches['cookie']);

        $header['errno']   = $err;
        $header['errmsg']  = $errmsg;
        $header['headers']  = $header_content;
        $header['content'] = $body_content;
        $header['cookies'] = $cookiesOut;
    return $header;

【讨论】:

非常有用的傻瓜代码。有用。谢谢你。如果人们想要 HTTP 状态代码,请使用以下语法:$header['http_code']; @Doug,我在here 中检查了您的代码的正则表达式#Set-Cookie:\\s+(?&lt;cookie&gt;[^=]+=[^;]+)#m,正则表达式引擎将这部分\\s+ 解释为:\` matches the character \ literally s+` 匹配字符 s 字面意思(区分大小写)——这不是正则表达式的错误,不应该是\s+(只有一个反斜杠)吗? @Igor,\\ 是因为在 PHP the \ is viewed as an escape character 中处理字符串时。因此,您需要两个斜杠来等于评估字符串中的一个斜杠(然后将其用于 RegEx 匹配)。从 RegEx 的角度来看,您是正确的,我只想要一个斜杠。【参考方案3】:

上述解决方案,即使具有唯一的 CookieFile 名称,也会在规模上造成很多问题。

我们必须使用此解决方案提供大量身份验证,并且由于高文件读写操作,我们的服务器出现故障。

解决方案是使用 Apache 反向代理并完全忽略 CURL 请求。

如何在 Apache 上使用 Proxy 的详细信息可以在这里找到: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html

【讨论】:

【参考方案4】:

在这里您可以找到一些关于 cURL 和 cookie http://docstore.mik.ua/orelly/webprog/pcook/ch11_04.htm 的有用信息。

你也可以像函数一样使用这个做得好的方法https://github.com/alixaxel/phunction/blob/master/phunction/Net.php#L89:

function CURL($url, $data = null, $method = 'GET', $cookie = null, $options = null, $retries = 3)

    $result = false;

    if ((extension_loaded('curl') === true) && (is_resource($curl = curl_init()) === true))
    
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_FAILONERROR, true);
        curl_setopt($curl, CURLOPT_AUTOREFERER, true);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

        if (preg_match('~^(?:DELETE|GET|HEAD|OPTIONS|POST|PUT)$~i', $method) > 0)
        
            if (preg_match('~^(?:HEAD|OPTIONS)$~i', $method) > 0)
            
                curl_setopt_array($curl, array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true));
            

            else if (preg_match('~^(?:POST|PUT)$~i', $method) > 0)
            
                if (is_array($data) === true)
                
                    foreach (preg_grep('~^@~', $data) as $key => $value)
                    
                        $data[$key] = sprintf('@%s', rtrim(str_replace('\\', '/', realpath(ltrim($value, '@'))), '/') . (is_dir(ltrim($value, '@')) ? '/' : ''));
                    

                    if (count($data) != count($data, COUNT_RECURSIVE))
                    
                        $data = http_build_query($data, '', '&');
                    
                

                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            

            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));

            if (isset($cookie) === true)
            
                curl_setopt_array($curl, array_fill_keys(array(CURLOPT_COOKIEJAR, CURLOPT_COOKIEFILE), strval($cookie)));
            

            if ((intval(ini_get('safe_mode')) == 0) && (ini_set('open_basedir', null) !== false))
            
                curl_setopt_array($curl, array(CURLOPT_MAXREDIRS => 5, CURLOPT_FOLLOWLOCATION => true));
            

            if (is_array($options) === true)
            
                curl_setopt_array($curl, $options);
            

            for ($i = 1; $i <= $retries; ++$i)
            
                $result = curl_exec($curl);

                if (($i == $retries) || ($result !== false))
                
                    break;
                

                usleep(pow(2, $i - 2) * 1000000);
            
        

        curl_close($curl);
    

    return $result;

并将其作为$cookie 参数传递:

$cookie_jar = tempnam('/tmp','cookie');

【讨论】:

【参考方案5】:

您可以使用 curl opt 指定 cookie 文件。您可以为每个用户使用一个唯一的文件。

curl_setopt( $curl_handle, CURLOPT_COOKIESESSION, true );
curl_setopt( $curl_handle, CURLOPT_COOKIEJAR, uniquefilename );
curl_setopt( $curl_handle, CURLOPT_COOKIEFILE, uniquefilename );

处理它的最佳方法是将您的请求逻辑粘贴到 curl 函数中,并将唯一的文件名作为参数传递。

    function fetch( $url, $z=null ) 
            $ch =  curl_init();

            $useragent = isset($z['useragent']) ? $z['useragent'] : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2';

            curl_setopt( $ch, CURLOPT_URL, $url );
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
            curl_setopt( $ch, CURLOPT_AUTOREFERER, true );
            curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
            curl_setopt( $ch, CURLOPT_POST, isset($z['post']) );

            if( isset($z['post']) )         curl_setopt( $ch, CURLOPT_POSTFIELDS, $z['post'] );
            if( isset($z['refer']) )        curl_setopt( $ch, CURLOPT_REFERER, $z['refer'] );

            curl_setopt( $ch, CURLOPT_USERAGENT, $useragent );
            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, ( isset($z['timeout']) ? $z['timeout'] : 5 ) );
            curl_setopt( $ch, CURLOPT_COOKIEJAR,  $z['cookiefile'] );
            curl_setopt( $ch, CURLOPT_COOKIEFILE, $z['cookiefile'] );

            $result = curl_exec( $ch );
            curl_close( $ch );
            return $result;
    

我用它来快速抓取。它需要 url 和一个选项数组。

【讨论】:

【参考方案6】:

您可以使用CURLOPT_COOKIEFILECURLOPT_COOKIEJAR 为每个用户定义不同的cookie。为每个用户制作不同的文件,这样每个用户都可以在远程服务器上拥有自己的基于 cookie 的会话。

【讨论】:

当这个页面被大量用户(超过一万)使用时怎么办?这对服务器来说不是很大的负载吗?

以上是关于PHP 卷曲和 Cookie的主要内容,如果未能解决你的问题,请参考以下文章

PHP 卷曲和 Cookie

PHP 卷曲标头

处理大卷曲响应 - PHP

php 卷曲 - PHP认证

php 卷曲类PHP

PHP PHP卷曲