简体   繁体   English

cURL 从终端工作,但不是从 PHP

[英]cURL works from Terminal, but not from PHP

I'm running into a rather strange issue.我遇到了一个相当奇怪的问题。

I'm trying to log into a remote moodle install using curl from PHP.我正在尝试使用来自 PHP 的 curl 登录远程 moodle 安装。

I have a curl command, which works perfectly in the Terminal.我有一个 curl 命令,它在终端中完美运行。

When I translate the same thing into PHP, it works, but it just doesn't login.当我将同样的东西翻译成 PHP 时,它可以工作,但就是无法登录。 The exact same value which successfully login via terminal, somehow trips up the login system via PHP and it doesn't login.通过终端成功登录的完全相同的值,以某种方式通过 PHP 启动登录系统并且它没有登录。 Instead, it returns the login page again.相反,它会再次返回登录页面。

My cURL command (data section ommitted as it has my username and password):我的 cURL 命令(省略了数据部分,因为它有我的用户名和密码):

curl 'http://moodle.tsrs.org/login/index.php'
-H 'Pragma: no-cache'
-H 'Origin: http://moodle.tsrs.org'
-H 'Accept-Encoding: gzip, deflate'
-H 'Accept-Language: en-US,en;q=0.8'
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36'
-H 'Content-Type: application/x-www-form-urlencoded'
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
-H 'Cache-Control: no-cache'
-H 'Referer: http://moodle.tsrs.org/login/index.php'
-H 'Cookie: MoodleSession=ngcidh028m37gm8gbdfe07mvs7; MOODLEID_=%25F1%25CD%2519D%25B2k%25FE%251D%25EFH%25E5t%25B1%2503%258E; MoodleSessionTest=NhzaTNij6j; _ga=GA1.2.925953522.1416155774; _gat=1; __utmt=1; __utma=147409963.925953522.1416155774.1416642544.1416692798.3; __utmb=147409963.1.10.1416692798; __utmc=147409963; __utmz=147409963.1416155774.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)'
-H 'Connection: keep-alive'

The corresponding PHP code:对应的PHP码:

function login() {
    $username = $_POST['username'];
    $password = $_POST['password'];

    if(!isset($_POST['username']) || !isset($_POST['password'])) {
        echo "No login data received";
        return;
    }

    $creq = curl_init();

    $data = array('username' => $username, 'password' => $password, 'testcookies'=> '1');

    $headers = array('Pragma: no-cache', 'Origin: http://moodle.tsrs.org', 'Accept-Encoding: ', 'Accept-Language: en-US,en;q=0.8', 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36', 'Content-Type: application/x-www-form-urlencoded', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Cache-Control: no-cache', 'Cookie: MoodleSession=ngcidh028m37gm8gbdfe07mvs7; MOODLEID_=%25F1%25CD%2519D%25B2k%25FE%251D%25EFH%25E5t%25B1%2503%258E; MoodleSessionTest=NhzaTNij6j; _ga=GA1.2.925953522.1416155774; _gat=1; __utmt=1; __utma=147409963.925953522.1416155774.1416642544.1416692798.3; __utmb=147409963.1.10.1416692798; __utmc=147409963; __utmz=147409963.1416155774.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'Connection: keep-alive' );
        curl_setopt_array($creq, array(
        CURLOPT_URL => 'http://moodle.tsrs.org/login/index.php',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_ENCODING => '',
        CURLINFO_HEADER_OUT => true,
        CURLOPT_POSTFIELDS => $data,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_FOLLOWLOCATION => false
    ));

    $output = curl_exec($creq);

    echo print_r(curl_getinfo($creq));

    echo "\n" . $output . "\n";
}

And the output of curlinfo:和 curlinfo 的 output:

Array
(
    [url] => http://moodle.tsrs.org/login/index.php
    [content_type] => text/html; charset=utf-8
    [http_code] => 200
    [header_size] => 541
    [request_size] => 945
    [filetime] => -1
    [ssl_verify_result] => 0
    [redirect_count] => 0
    [total_time] => 1.462409
    [namelookup_time] => 0.002776
    [connect_time] => 0.330766
    [pretransfer_time] => 0.330779
    [size_upload] => 365
    [size_download] => 8758
    [speed_download] => 5988
    [speed_upload] => 249
    [download_content_length] => -1
    [upload_content_length] => 365
    [starttransfer_time] => 0.694866
    [redirect_time] => 0
    [certinfo] => Array
        (
        )

    [primary_ip] => 125.22.33.149
    [redirect_url] =>
    [request_header] => POST /login/index.php HTTP/1.1
Host: moodle.tsrs.org
Pragma: no-cache
Origin: http://moodle.tsrs.org
Accept-Language: en-US,en;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Cache-Control: no-cache
Cookie: MoodleSession=ngcidh028m37gm8gbdfe07mvs7; MOODLEID_=%25F1%25CD%2519D%25B2k%25FE%251D%25EFH%25E5t%25B1%2503%258E; MoodleSessionTest=NhzaTNij6j; _ga=GA1.2.925953522.1416155774; _gat=1; __utmt=1; __utma=147409963.925953522.1416155774.1416642544.1416692798.3; __utmb=147409963.1.10.1416692798; __utmc=147409963; __utmz=147409963.1416155774.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Connection: keep-alive
Content-Length: 365
Expect: 100-continue
Content-Type: application/x-www-form-urlencoded; boundary=----------------------------83564ee60d56


)

Does anyone know any possible reason for this?有谁知道这可能的原因吗? I've tried swapping out the hard coded cookie with COOKIEFILE and COOKIEJAR, but it doesn't change anything.我试过用 COOKIEFILE 和 COOKIEJAR 替换硬编码的 cookie,但它没有改变任何东西。

This could have been debugged better by seeing everything that was actually done by cURL. 通过查看cURL实际完成的所有内容 ,可以更好地调试它。 This is done by adding the verbose flag to the command: -v . 这是通过向命令添加verbose标志来完成的: -v

$ curl localhost/login [...] -v

We can get the same output from PHP's curl by adding the CURLOPT_VERBOSE option. 我们可以通过添加CURLOPT_VERBOSE选项从PHP的卷曲中获得相同的输出。 Note that by adding this line you are instructing cURL to output the same information to STDOUT - it will not be returned and content will not be sent to the browser, so this must be debugged in the terminal. 请注意,通过添加此行,您将指示cURL向STDOUT输出相同的信息 - 它不会被返回,内容也不会被发送到浏览器,因此必须在终端中调试。

curl_setopt($curl, CURLOPT_VERBOSE, 1);

By doing it this way, you can get a consistent and comparable output of both HTTP requests, it should look sommthing like this: 通过这种方式,您可以获得两个HTTP请求的一致且可比较的输出,它应该看起来像这样:

POST / HTTP/1.1
Host: localhost:3000
Pragma: no-cache
Origin: http://moodle.tsrs.org
Accept-Language: en-US,en;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Cache-Control: no-cache
Cookie: MoodleSession=ngcidh028m37gm8gbdfe07mvs7; MOODLEID_=%25F1%25CD%2519D%25B2k%25FE%251D%25EFH%25E5t%25B1%2503%258E; MoodleSessionTest=NhzaTNij6j; _ga=GA1.2.925953522.1416155774; _gat=1; __utmt=1; __utma=147409963.925953522.1416155774.1416642544.1416692798.3; __utmb=147409963.1.10.1416692798; __utmc=147409963; __utmz=147409963.1416155774.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Connection: keep-alive
Content-Length: 250
Expect: 100-continue
Content-Type: application/x-www-form-urlencoded; boundary=------------------------b4d79f17a3887f2d

< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 2
< ETag: W/"2-mZFLkyvTelC5g8XnyQrpOw"
< Date: Thu, 22 Dec 2016 19:13:40 GMT
< Connection: keep-alive

Left: Command line cURL as provided in the question (with extra -v flag) 左:问题中提供的命令行cURL(带有额外的-v标志)

Right: PHP cURL as posted in the question (with CURLOUT_VERBOSE enabled) 右:在问题中发布的PHP cURL(启用了CURLOUT_VERBOSE

As you can see, the headers aren't the same, and this makes that clear. 正如您所看到的,标题一样,这就清楚了。 The PHP invocation is missing Accept-Encoding and Referer headers. PHP调用缺少Accept-EncodingReferer标头。

并排比较命令行与php curl输出的curl


If that didn't turn up anything, let's try changing some more cURL settings in PHP back to the original cURL defaults. 如果没有出现任何问题,让我们尝试将PHP中的更多cURL设置更改回原始的cURL默认值。

Internally, PHP opts to override some defaults in cURL without telling you. 在内部,PHP选择在不告诉您的情况下覆盖cURL中的某些默认值 While these settings should be fine, let's change them back by explicitly reseting them back to cURL defaults: 虽然这些设置应该没问题,但让我们通过明确地将它们重新设置为cURL默认值来更改它们:

curl_setopt($curl, CURLOPT_DNS_CACHE_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0);
curl_setopt($curl, CURLOPT_MAXREDIRS, -1);
curl_setopt($curl, CURLOPT_NOSIGNAL, 0);

Use http_build_query on the $data array before passing to curl to avoid Content-Type: application/x-www-form-urlencoded; boundary=--- 在传递给curl之前在$data数组上使用http_build_query以避免Content-Type: application/x-www-form-urlencoded; boundary=--- Content-Type: application/x-www-form-urlencoded; boundary=--- . Content-Type: application/x-www-form-urlencoded; boundary=--- This also ensures to encode any special characters from the password. 这还可以确保对密码中的任何特殊字符进行编码。

curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

Reshape your curl requests as follows: 重塑您的卷曲请求,如下所示:

Make a GET request to the login page with pointing a cookie file at $cookies = '/tmp/some/dir/xyz.cookie.txt' . 向登录页面发出GET请求,并将cookie文件指向$cookies = '/tmp/some/dir/xyz.cookie.txt' Make sure using full path for cookie name. 确保使用cookie名称的完整路径。 And then close the curl handle. 然后关闭卷曲手柄。 This will store the cookie in cookie file. 这会将cookie存储在cookie文件中。

$creq = curl_init();
curl_setopt_array($creq, array(
  CURLOPT_URL => 'http://moodle.tsrs.org/login/index.php',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLINFO_HEADER_OUT => true,
  CURLOPT_HTTPHEADER => $headers,
  CURLOPT_FOLLOWLOCATION => false,
  CURLOPT_COOKIEJAR => $cookies // save cookie
));
$output = curl_exec($creq);
curl_close($creq);

Now make the POST request with second curl request. 现在使用第二个curl请求发出POST请求。 This time point the same cookie file with COOKIEFILE option. 这个时间点与COOKIEFILE选项相同的cookie文件。

$creq = curl_init();
curl_setopt_array($creq, array(
  CURLOPT_URL => 'http://moodle.tsrs.org/login/index.php',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_ENCODING => '',
  CURLINFO_HEADER_OUT => true,
  CURLOPT_POSTFIELDS => http_build_query ($data),
  CURLOPT_HTTPHEADER => $headers,
  CURLOPT_FOLLOWLOCATION => false,
  CURLOPT_COOKIEJAR => $cookies, // save cookie
  CURLOPT_COOKIEFILE => $cookies // load cookie
);
$output = curl_exec($creq);
curl_close($creq);

It can happen sometimes the server look for the cookie when a login request made (to ensure that the request came after visiting the login page). 有时服务器会在发出登录请求时查找cookie(以确保在访问登录页面后发出请求)。

Most likely your problem is related to HTTP header Expect: 100-continue that cURL sends by default for each POST request. 很可能您的问题与HTTP标头有关Expect: 100-continue ,默认情况下,cURL为每个POST请求发送。

The Expect: 100-continue header is used in POST requests containing big data when client is not sure that server will accept such request. Expect: 100-continue当客户端不确定服务器是否接受此类请求时,在包含大数据的POST请求中使用Expect: 100-continue标头。 In this case client first sends request with only headers including Expect: 100-continue and, if the server's response is successful, send the same request with body (POST data). 在这种情况下,客户端首先发送只包含Expect: 100-continue头的请求,如果服务器的响应成功,则发送与body(POST数据)相同的请求。

The problem is that not all web servers handle this header correctly. 问题是并非所有Web服务器都正确处理此标头。 In such cases sending this header is undesired. 在这种情况下,发送此标头是不受欢迎的。

The solution is manually remove Expect header from sending headers by passing array('Expect:') to CURLOPT_HTTPHEADER option. 解决方案是通过将array('Expect:') CURLOPT_HTTPHEADERCURLOPT_HTTPHEADER选项来手动删除Expect标头以发送标头。 In your case you can simply add 'Expect:' string to $headers array: 在您的情况下,您只需将'Expect:'字符串添加到$headers数组:

$headers[] = 'Expect:';

I solved the issue by setting a User-Agent我通过设置用户代理解决了这个问题

$headers = array(
        'Accept: */*',
        'User-Agent: curl/7.68.0',
        'Accept-Encoding: deflate,gzip,br',
        'Content-Type:application/json',
);

I suspect your first attempt using the curl command is using the GET method in the index.php file. 我怀疑你第一次尝试使用curl命令是在index.php文件中使用GET方法。 I suggest you enable --trace-ascii on your first curl request in the command line and see whether a GET request is being made by the page or not. 我建议你在命令行的第一个curl请求中启用--trace-ascii ,看看页面是否正在发出GET请求。 If yes, you should change your PHP script which is using the POST method. 如果是,您应该更改使用POST方法的PHP脚本。 If you change the CURLOPT_POST to false, the PHP script should work. 如果将CURLOPT_POST更改为false,则PHP脚本应该可以正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM