Як отримати IP-адресу клієнта в Laravel 5+


136

Я намагаюся отримати IP-адресу клієнта в Laravel.

Отримати IP клієнта в PHP легко, використовуючи $_SERVER["REMOTE_ADDR"]. Він працює чудово в основному PHP, але коли я використовую те ж саме в Laravel, він повертає IP сервера замість IP відвідувача.

Відповіді:


194

Переглядаючи API Laravel :

Request::ip();

Внутрішньо він використовує getClientIpsметод із об’єкта запиту 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
Використання об'єкта Request для мене не працює, він повертає адресу мого сервера Homestead. 192.168.10.10, що явно не моя IP-адреса.
Вінс Кронлейн

@VinceKronlein для вашої справи перевірте цю відповідь stackoverflow.com/a/41769505/3437790
Себастьян Хорін

3
@VinceKronlein у вашому випадку це було дуже правильно. Оскільки ви зверталися до Homestead, у вашій локальній мережі ви мали 192. IP. якщо ви зверталися до чужого домашнього сервера через Інтернет, ваш IP-код буде виходити через ваш Інтернет-провайдер, і ваш загальнодоступний.
ied3vil

83

Якщо ви знаходитесь під балансиром навантаження, Laravel \Request::ip() завжди повертає IP-адресу балансира:

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

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

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

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

Цей спеціальний метод повертає реальний клієнт ip:

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;
                }
            }
        }
    }
}

На додаток до цього, я пропоную вам бути дуже обережними, використовуючи проміжне програмне забезпечення дросельної заслінки Laravel : воно використовує Laravel'sRequest::ip() , так що всі ваші відвідувачі будуть ідентифіковані як один і той же користувач, і ви дуже швидко досягнете межі дроселя. Я пережив це наживо, і це спричинило великі проблеми.

Щоб виправити це:

Освітлити \ Http \ Request.php

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

Тепер ви також можете використовувати Request::ip(), що повинно повернути реальний IP у виробництві.


1
чи правильно, якщо (filter_var ...) всередині другого передбачення? цей код ніколи не буде виконуватися.
Mistre83

@ Mistre83 Так, ти маєш рацію, я думаю, що це тестовий нагляд. Я його оновлюю!
Себастьєн Хорін

6
це насправді працює з laravel 5.4. Будь ласка, подумайте про створення PR на github. Я думаю, що це має бути поведінка за замовчуванням
Crystal

1
Це спрацювало в Ларавелі 5.3, коли метод ip () об'єкта запиту Laravel продовжував повертати 127.0.0.1
w5m

3
Не можете виправити це за допомогою надійних проксі? - laravel.com/docs/master/requests#configuring-trusted-proxies
користувач2722667

74

Використовуйте request()->ip().

З того, що я розумію, оскільки Laravel 5 радить / корисною практикою використовувати глобальні функції, такі як:

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

І, якщо нічого, при використанні функцій замість статичних позначень мій IDE не світиться, як ялинка.


3
Ви маєте рацію, що requestце "глобальна" функція - це одна з глобальних хелперних функцій, яку надає laravel. Однак запит фасад, не є статичним (ні метод ф) - request()->fooі Reqest::fooта $request->fooвсе однакові. Погляньте на приклад, наприклад: gist.github.com/cjke/026e3036c6a10c672dc5
Кріс

1
Досить справедливо - обидва однаково правильно. Я просто подумав, куди ви сказали, "це Request::ipможе не ввести в оману
Кріс

3
Проблема полягає в тому, що ці глобальні функції непросто перевірити - з них не можна знущатися. Фасади можуть бути. Я намагаюся уникати глобальних функцій, оскільки це означає перекопатися до глобального джерела функції, щоб знущатися над його дзвінками, що є додатковою роботою, дратівливою і не повинно бути моєю відповідальністю.
hackel

1
Хоча request()->ip()це правильно, навколишній текст дійсно вводить в оману - особливо сказати "це не так Request::ip."
Кріс

1
@Chris Спасибі, ви абсолютно праві. Відредаговано для наочності!
Стен Смулдерс

27

Додати простір імен

use Request;

Потім викличте функцію

Request::ip();

1
Якщо у вас є простір імен: -> використовувати Illuminate \ Http \ Request; сміливий Перейменуйте простір імен для запиту, оскільки обидва зіткнуться
shalini

Оригінальна відповідь правильна. Вам потрібно імпортувати, use Requestоскільки ви намагаєтесь використовувати Фасад. Простір імен, який ви вказали, призначений для базового класу. Якщо ви імпортуєте, що ви отримаєте помилку, оскільки ip()статично її не можна назвати, саме для цього і фасад.
jfadich

Якщо ви збираєтеся турбувати імпортувати клас, ви повинні використовувати фактичний фасад, а НЕ псевдонім: use Illuminate\Support\Facades\Request. Якщо ні, просто використовуйте \Request::.
hackel

18

Для Laravel 5 можна використовувати об’єкт Запит. Просто назвіть його ip()метод, щось на зразок:

$request->ip();


12

Про це слід подбати дві речі:

  1. Отримайте допоміжну функцію, яка повертає a Illuminate\Http\Requestі викликайте ->ip()метод:

    request()->ip();
  2. Подумайте про конфігурацію вашого сервера, він може використовувати проксі-сервер або load-balancer, особливо, у конфігурації AWS ELB.

Якщо це ваш випадок, вам потрібно дотримуватися " Налаштування надійних проксі-серверів " або, можливо, навіть встановити параметр "Довіряти всім проксі".

Чому? Оскільки ваш сервер отримає ваш проксі /load-balancer IP.

Якщо ви перебуваєте на балансовому навантажувачі AWS, перейдіть до App\Http\Middleware\TrustProxiesта зробіть $proxiesдекларацію таким чином:

protected $proxies = '*';

Тепер протестуйте його та відсвяткуйте, оскільки ви просто врятували себе від проблем із проміжним програмним забезпеченням дросельної заслінки. Це також спирається наrequest()->ip() та без встановлення "TrustProxies". Ви можете заблокувати вхід у систему, а не блокувати лише IP-адресу винного.

Оскільки проміжне програмне забезпечення для дроселів не пояснено належним чином у документації, я рекомендую переглянути " підручник laravel 5.2 для початківців, обмеження швидкості API "

Випробуваний у Laravel 5.7


7

У Laravel 5.4 ми не можемо назвати ip статикою. Це правильний спосіб отримати IP користувача:

 use Illuminate\Http\Request;

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

7

Якщо ви зателефонуєте на цю функцію, то ви легко отримаєте IP-адресу клієнта. Я вже використовував це в своєму існуючому проекті:

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

Якщо ви все ще отримуєте 127.0.0.1 як IP, вам потрібно додати свій "проксі", але пам’ятайте, що вам потрібно змінити його перед початком виробництва!

Прочитайте " Налаштування надійних проксі ".

І додайте це:

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

Тепер request()->ip()дає вам правильний IP.


4

Якщо ви хочете, щоб клієнтський IP був і ваш сервер був за Aws Elb, тоді користуйтеся наступним кодом. Випробуваний на laravel 5.3

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

1
Більше не працює, тепер потрібен "trustHeaderSet"
Shadrix

Для "останніх" версій laravel дивіться документи laravel.com/docs/5.5/requests#configuring-trusted-proxies
Сандра

0

Якщо у вас є декілька проксі-серверів, як CDN + Load Balancer.
Використання функції Laravel Request :: ip () отримає найпростіший IP-проксі, але не клієнтський IP.
Ви можете спробувати наступне рішення.

app / Http / Middleware / TrustProxies.php

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

Довідка: https://github.com/fideloper/TrustedProxy/isissue/107#issuecomment-373065215


0

Я використовував функцію Sebastien Horin getIp та request () -> ip () (на глобальний запит), тому що для localhost функція getIp return null:

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

Функція 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
  $ip = $_SERVER['REMOTE_ADDR'];

1
Це допомагає більше, якщо ви надасте пояснення, чому це найкраще рішення та поясніть, як воно працює. Ми хочемо вчити, а не просто надавати код. Оскільки система позначає її як низькоякісну, тому спробуйте її вдосконалити.
Олов'яний чоловік

Дякую за пропозицію
rahedcs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.