Cách nhận địa chỉ IP của khách hàng trong Laravel 5+


136

Tôi đang cố gắng lấy địa chỉ IP của khách hàng ở Laravel.

Thật dễ dàng để có được IP của khách hàng trong PHP bằng cách sử dụng $_SERVER["REMOTE_ADDR"]. Nó hoạt động tốt trong PHP lõi, nhưng khi tôi sử dụng điều tương tự trong Laravel, nó sẽ trả về IP máy chủ thay vì IP của khách truy cập.

Câu trả lời:


194

Nhìn vào API của Laravel :

Request::ip();

Trong nội bộ, nó sử dụng getClientIpsphương thức từ Đối tượng yêu cầu Symfony :

public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($ip);
} 

3
Sử dụng đối tượng Request không hoạt động với tôi, nó trả về địa chỉ máy chủ Homestead của tôi. 192.168.10.10 rõ ràng không phải là địa chỉ IP của tôi.
Vince Kronlein

@VinceKronlein cho trường hợp của bạn kiểm tra câu trả lời này stackoverflow.com/a/41769505
Sebastien Horin

3
@VinceKronlein trong trường hợp của bạn thì nó rất đúng. Bởi vì bạn đang truy cập Homestead, trong mạng ĐỊA PHƯƠNG của bạn, bạn đã có 192. IP. nếu bạn đang truy cập máy chủ homestead của người khác, thông qua internet, IP của bạn sẽ đi qua ISP của bạn và mạng công cộng của bạn sẽ được sử dụng.
ied3vil

83

Nếu bạn ở dưới bộ cân bằng tải, thì Laravel \Request::ip() luôn trả về IP của bộ cân bằng:

            echo $request->ip();
            // server ip

            echo \Request::ip();
            // server ip

            echo \request()->ip();
            // server ip

            echo $this->getIp(); //see the method below
            // clent ip

Phương thức tùy chỉnh này trả về ip máy khách thực sự:

public function getIp(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

Ngoài ra, tôi khuyên bạn nên hết sức cẩn thận khi sử dụng phần mềm trung gian bướm ga của Laravel : Nó cũng sử dụng của Laravel Request::ip(), vì vậy tất cả khách truy cập của bạn sẽ được xác định là cùng một người dùng và bạn sẽ nhanh chóng đạt đến giới hạn bướm ga. Tôi đã trải nghiệm trực tiếp và điều này gây ra vấn đề lớn.

Để khắc phục điều này:

Chiếu sáng \ http \ Request.php

    public function ip()
    {
        //return $this->getClientIp(); //original method
        return $this->getIp(); // the above method
    }

Bây giờ bạn cũng có thể sử dụng Request::ip(), sẽ trả lại IP thực trong sản xuất.


1
là chính xác if (filter_var ...) trong foreach thứ hai? mã này sẽ không bao giờ được thực thi.
Mistre83

@ Mistre83 Có bạn đúng, tôi nghĩ đó là một giám sát thử nghiệm. Tôi cập nhật nó!
Sebastien Horin

6
điều này thực sự hoạt động với laravel 5.4 Hãy xem xét thực hiện PR trên github. Tôi nghĩ rằng đây phải là hành vi mặc định
Crystal

1
Điều này có hiệu quả trong một điều trị trong Laravel 5.3 khi phương thức ip () của đối tượng yêu cầu Laravel tiếp tục trả về 127.0.0.1
w5m

3
Bạn có thể sửa lỗi này với proxy đáng tin cậy không? - laravel.com/docs/master/requests#configuring-trusty-proxies
user2722667

74

Sử dụng request()->ip().

Theo những gì tôi hiểu, kể từ khi Laravel 5 khuyên bạn nên sử dụng các chức năng toàn cầu như:

response()->json($v);
view('path.to.blade');
redirect();
route();
cookie();

Và, nếu có bất cứ điều gì, khi sử dụng các chức năng thay vì ký hiệu tĩnh, IDE của tôi không sáng lên như cây thông Noel.


3
Bạn đúng đó requestlà một chức năng "toàn cầu" - đó là một trong những chức năng trợ giúp toàn cầu được cung cấp bởi laravel. Tuy nhiên, yêu cầu mặt tiền, không phải là tĩnh (cũng không phải là phương pháp ip) - request()->foo, và Reqest::foo$request->footất cả đều giống hệt nhau. Hãy xem ý chính này để biết ví dụ: gist.github.com/cjke/026e3036c6a10c672dc5
Chris

1
Đủ công bằng - cả hai đều đúng như nhau. Tôi chỉ nghĩ một chút khi bạn nói "nó Request::ipcó thể không gây hiểu lầm
Chris

3
Vấn đề là các chức năng toàn cầu này không dễ bị kiểm tra, chúng không thể bị chế giễu. Mặt tiền có thể được. Tôi cố gắng tránh các chức năng toàn cầu, vì điều đó có nghĩa là đào qua nguồn chức năng toàn cầu để chế nhạo các cuộc gọi của nó, đây là công việc phụ, gây phiền nhiễu và không phải là trách nhiệm của tôi.
hackel

1
Mặc dù request()->ip()là chính xác, văn bản xung quanh thực sự sai lệch - đặc biệt là nói "không phải vậy Request::ip.
Chris

1
@Chris Cảm ơn, bạn hoàn toàn đúng. Chỉnh sửa cho rõ ràng!
Stan Smulder

27

Thêm không gian tên

use Request;

Sau đó gọi hàm

Request::ip();

1
Nếu bạn đã sử dụng không gian tên: -> sử dụng Illuminate \ http \ Request; đậm namespace Rename cho yêu cầu kể từ khi cả hai sẽ xung đột
Shalini

Câu trả lời ban đầu là chính xác. Bạn cần nhập use Requestvì bạn đang cố sử dụng Mặt tiền. Không gian tên bạn cung cấp là dành cho lớp bên dưới. Nếu bạn nhập rằng bạn sẽ gặp lỗi vì ip()không thể được gọi là tĩnh, đó là những gì mặt tiền dành cho.
jfadich

Nếu bạn định nhập lớp, bạn nên sử dụng mặt tiền thực tế chứ không phải bí danh : use Illuminate\Support\Facades\Request. Nếu không, chỉ cần sử dụng \Request::.
hackel

18

Đối với Laravel 5, bạn có thể sử dụng đối tượng Yêu cầu. Chỉ cần gọi ip()phương thức của nó , đại loại như:

$request->ip();


12

Có hai điều cần quan tâm:

  1. Nhận một hàm trợ giúp trả về a Illuminate\Http\Requestvà gọi ->ip()phương thức:

    request()->ip();
  2. Hãy nghĩ về cấu hình máy chủ của bạn, nó có thể sử dụng proxy hoặc load-balancerđặc biệt là trong cấu hình AWS ELB.

Nếu đây là trường hợp của bạn, bạn cần tuân theo "Định cấu hình proxy đáng tin cậy " hoặc thậm chí có thể đặt tùy chọn "Tin cậy tất cả các proxy".

Tại sao? Bởi vì là máy chủ của bạn sẽ nhận được proxy / load-balancerIP của bạn thay vào đó.

Nếu bạn đang sử dụng trình tải cân bằng AWS, hãy truy cập App\Http\Middleware\TrustProxiesvà thực hiện $proxieskhai báo như sau:

protected $proxies = '*';

Bây giờ hãy kiểm tra nó và ăn mừng vì bạn vừa tự cứu mình khỏi gặp rắc rối với phần mềm trung gian điều tiết. Nó cũng dựa vào request()->ip()và không thiết lập "TrustProxies", bạn có thể chặn tất cả người dùng của mình đăng nhập thay vì chỉ chặn IP của thủ phạm.

Và vì phần mềm trung gian điều tiết không được giải thích chính xác trong tài liệu, tôi khuyên bạn nên xem " hướng dẫn laravel 5.2 cho người mới bắt đầu, Giới hạn tỷ lệ API "

Đã thử nghiệm ở Laravel 5.7


7

Trong Laravel 5.4, chúng ta không thể gọi ip tĩnh. Đây là một cách chính xác để có được IP của người dùng:

 use Illuminate\Http\Request;

public function contactUS(Request $request)
    {
        echo $request->ip();
        return view('page.contactUS');
    }

7

Nếu bạn gọi chức năng này thì bạn dễ dàng lấy địa chỉ IP của khách hàng. Tôi đã sử dụng điều này trong dự án hiện tại của tôi:

public function getUserIpAddr(){
       $ipaddress = '';
       if (isset($_SERVER['HTTP_CLIENT_IP']))
           $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
       else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
           $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
       else if(isset($_SERVER['HTTP_X_FORWARDED']))
           $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
       else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
           $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
       else if(isset($_SERVER['HTTP_FORWARDED']))
           $ipaddress = $_SERVER['HTTP_FORWARDED'];
       else if(isset($_SERVER['REMOTE_ADDR']))
           $ipaddress = $_SERVER['REMOTE_ADDR'];
       else
           $ipaddress = 'UNKNOWN';    
       return $ipaddress;
    }

5

Nếu bạn vẫn nhận được 127.0.0.1 làm IP, bạn cần thêm "proxy" của mình, nhưng lưu ý rằng bạn phải thay đổi nó trước khi đi vào sản xuất!

Đọc " Cấu hình proxy đáng tin cậy ".

Và thêm điều này:

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array
     */
    protected $proxies = '*';

Bây giờ request()->ip()cung cấp cho bạn IP chính xác.


4

Nếu bạn muốn IP của máy khách và máy chủ của bạn đứng sau aws elb, thì hãy sử dụng mã sau đây. Đã kiểm tra ấu trùng 5.3

$elbSubnet = '172.31.0.0/16';
Request::setTrustedProxies([$elbSubnet]);
$clientIp = $request->ip();

1
Không hoạt động nữa, bây giờ "TrustedHeadset" đã được xem
Shadrix

để biết các phiên bản laravel "gần đây", hãy xem tài liệu laravel.com/docs/5.5/requests#configuring-trusty-proxies
Sandra

0

Nếu bạn có proxy nhiều lớp giống như CDN + Load Balancer.
Sử dụng chức năng Laravel Request :: ip () sẽ nhận được IP proxy đúng nhất nhưng không phải IP máy khách.
Bạn có thể thử giải pháp sau đây.

ứng dụng / http / Middleware / TrustProxies.php

protected $proxies = ['0.0.0.0/0'];

Tham khảo: https://github.com/fideloper/TrustyProxy/issues/107#issuecomment-373065215


0

Tôi đã sử dụng hàm Sebastien Horin getIp và request () -> ip () (theo yêu cầu toàn cầu), bởi vì để localhost, hàm getIp trả về null:

$this->getIp() ?? request()->ip();

Hàm getIp:

public function getIp(){
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
    if (array_key_exists($key, $_SERVER) === true){
        foreach (explode(',', $_SERVER[$key]) as $ip){
            $ip = trim($ip); // just to be safe
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                return $ip;
            }
        }
    }
}

}


-2

Khi chúng tôi muốn người dùng ip_address:

$_SERVER['REMOTE_ADDR']

và muốn địa chỉ máy chủ:

$_SERVER['SERVER_ADDR']

-2
  $ip = $_SERVER['REMOTE_ADDR'];

1
Nó giúp nhiều hơn nếu bạn cung cấp một lời giải thích tại sao đây là giải pháp ưa thích và giải thích cách thức hoạt động của nó. Chúng tôi muốn giáo dục, không chỉ cung cấp mã. Như là, hệ thống đang đánh dấu nó là chất lượng thấp, vì vậy hãy cố gắng cải thiện nó.
Tin Man

Cảm ơn bạn đã gợi ý.
ban
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.