在Laravel 5+中如何获取客户端IP地址

183

我正在尝试在Laravel中获取客户端的IP地址。

在PHP中,使用$_SERVER["REMOTE_ADDR"]可以很容易地获取客户端的IP。 在核心PHP中使用这个方法是有效的,但是当我在Laravel中使用相同的方法时,它返回的是服务器的IP地址而不是访问者的IP地址。

21个回答

251

查看Laravel API

Request::ip();

在内部,它使用Symfony Request Object中的getClientIps方法:

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

9
使用请求对象对我没有用,它返回的是我的Homestead服务器地址192.168.10.10,显然不是我的IP地址。 - secondman
@VinceKronlein,针对您的情况,请查看此答案 https://dev59.com/tFwX5IYBdhLWcg3w4SzR#41769505 - Sebastien Horin
8
在您的情况下,@VinceKronlein是非常正确的。因为您正在访问Homestead,而且是在您的本地网络中,所以您有192. IP。如果您通过互联网访问其他人的Homestead服务器,那么您的IP将通过您的ISP传输,并使用您的公共IP地址。 - ied3vil
获取 PHP Laravel 中当前用户 IP:\Request::ip();或者$request->ip(); - Hassan Joseph

153
请注意:这个答案已经过时且有危险性。自Laravel 5.5以来,Request::ip()如果配置为信任负载均衡器的头部信息,将返回正确的IP地址。这里介绍的"自定义方法"允许客户端设置任意IP地址
如果您在负载均衡器下,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
            // client 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;
                }
            }
        }
    }
    return request()->ip(); // it will return the server IP if the client IP is not found using this method.
}

此外,我建议您在使用Laravel的throttle中间件时要非常小心:它也使用了Laravel的Request::ip(),因此所有访问者都会被识别为同一用户,很快就会达到限制。我在实际环境中遇到过这个问题,造成了很大的困扰。
为了解决这个问题:

Illuminate\Http\Request.php

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

现在你还可以使用Request::ip(),它应该在生产环境中返回真实的IP地址。

1
在第二个foreach循环内,if(filter_var...)是正确的吗?这段代码将永远不会被执行。 - Mistre83
11
这实际上适用于Laravel 5.4。 请考虑在GitHub上发起PR。我认为这应该成为默认行为。 - Crystal
1
当 Laravel 请求对象的 ip() 方法一直返回 127.0.0.1 时,这在 Laravel 5.3 中非常有效。 - w5m
5
你能否使用可信代理解决这个问题?- https://laravel.com/docs/master/requests#configuring-trusted-proxies - user2722667
2
对于答案的第二部分,编辑核心框架文件(Illuminate\Http\Request.php)是否明智?因为每次在另一台机器上执行composer install时,这都不会生效。 - Misbah Ahmad
显示剩余6条评论

81

使用 request()->ip()

据我了解,自Laravel 5以来,建议/良好的实践是使用全局函数,例如:

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

而且,使用函数时,相比静态符号,我的 IDE 不会像圣诞树一样亮起来。


4
你说得没错,request是一个“全局”函数 - 它是由Laravel提供的全局辅助函数之一。但是,Request门面不是静态的(方法ip也不是) - request()->fooReqest::foo$request->foo都是相同的。可以查看这个代码片段以获取示例:https://gist.github.com/cjke/026e3036c6a10c672dc5 - Chris
1
挺好的 - 两者都是正确的。我只是认为你说“它不是Request::ip”这部分可能会误导。 - Chris
3
问题在于这些全局函数不容易进行测试,无法进行模拟。而外观可以被模拟。我尽量避免使用全局函数,因为这意味着需要查找全局函数源代码以模拟其调用,这是额外的工作、令人烦恼的,并且不应该是我的责任。 - hackel
1
虽然request()->ip()是正确的,但周围的文字确实误导人,尤其是说“它不是Request::ip”。 - Chris
1
@Chris 谢谢,你说得完全正确。为了更加明确,我做了修改! - Stan Smulders
显示剩余4条评论

37

添加命名空间

use Request;

然后调用函数

Request::ip();

1
如果您使用了命名空间:--> use Illuminate\Http\Request; bold 由于两者会冲突,请重命名请求的命名空间 - shalini
原始答案是正确的。你需要导入 use Request,因为你正在尝试使用 Facade。你提供的命名空间是用于底层类的。如果你导入它,你将会得到一个错误,因为 ip() 不能被静态调用,这就是 Facade 的作用。 - jfadich
如果你要导入这个类,你应该使用实际的门面而不是别名:use Illuminate\Support\Facades\Request。如果不需要,只需使用\Request:: - hackel
Request::ip(); 返回服务器IP。我想要网络IP地址。这是可能的吗? - Suresh Kumar Kumawat

25

有两件事情需要注意:

  1. 编写一个帮助函数,返回 Illuminate\Http\Request 并调用 ->ip() 方法:

    request()->ip();
    
  2. 考虑您的服务器配置,它可能使用代理或负载均衡器,特别是在AWS ELB配置中。

如果您的情况是这样的,您需要遵循"配置受信任的代理",甚至可能设置一个“信任所有代理”的选项。

为什么?因为您的服务器将接收到代理/负载均衡器 IP。

如果您在AWS平衡负载器上,请转到 App\Http\Middleware\TrustProxies 并使$proxies声明如下:

protected $proxies = '*';

现在测试一下,庆祝一下吧,因为你刚刚避免了与 speed limiter 中间件的麻烦。 它还依赖于 request() -> ip() ,如果没有设置 "TrustProxies" ,则可能会阻止所有用户登录,而不仅仅是阻止罪犯的 IP。

由于文档没有很好地解释限速中间件,我建议观看 "laravel 5.2 初学者教程, API 速率限制"

在 Laravel 5.7 上测试通过


23

对于 Laravel 5,您可以使用 Request 对象。只需调用它的 ip() 方法,类似这样:

$request->ip();

19
在 Laravel 5 中。
public function index(Request $request) {
  $request->ip();
}

12

我在 Laravel 8.x 中进行了测试,您可以使用以下代码:

$request->ip()

用于获取客户端的IP地址。


8
在Laravel 5.4中,我们不能调用ip静态方法。下面是获取用户IP的正确方法:
 use Illuminate\Http\Request;

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

8

以下函数将帮助您获取客户端的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;
    }

1
这个答案是错误的。请阅读https://blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html。 - Your Common Sense

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接