PHP如何获取客户端IP

时间:2020-09-13

如果没有用到代理服务器

可以直接取 $_SERVER['REMOTE_ADDR'] 这个就是客户端的真实IP。

如果用到了代理服务器

需要先配置代理服务器来代理客户端IP

此时可以通过 $_SERVER["HTTP_CLIENT_IP"] $_SERVER["HTTP_X_FORWARDED_FOR"] 获取IP

下面是thinkphp框架获取IP的方法

/**
 * 获取客户端IP地址
 * @access public
 * @return string
 */
public function ip(): string
{
    if (!empty($this->realIP)) {
        return $this->realIP;
    }

    $this->realIP = $this->server('REMOTE_ADDR', '');

    // 如果指定了前端代理服务器IP以及其会发送的IP头
    // 则尝试获取前端代理服务器发送过来的真实IP
    $proxyIp       = $this->proxyServerIp;
    $proxyIpHeader = $this->proxyServerIpHeader;

    if (count($proxyIp) > 0 && count($proxyIpHeader) > 0) {
        // 从指定的HTTP头中依次尝试获取IP地址
        // 直到获取到一个合法的IP地址
        foreach ($proxyIpHeader as $header) {
            $tempIP = $this->server($header);

            if (empty($tempIP)) {
                continue;
            }

            $tempIP = trim(explode(',', $tempIP)[0]);

            if (!$this->isValidIP($tempIP)) {
                $tempIP = null;
            } else {
                break;
            }
        }

        // tempIP不为空,说明获取到了一个IP地址
        // 这时我们检查 REMOTE_ADDR 是不是指定的前端代理服务器之一
        // 如果是的话说明该 IP头 是由前端代理服务器设置的
        // 否则则是伪装的
        if (!empty($tempIP)) {
            $realIPBin = $this->ip2bin($this->realIP);

            foreach ($proxyIp as $ip) {
                $serverIPElements = explode('/', $ip);
                $serverIP         = $serverIPElements[0];
                $serverIPPrefix   = $serverIPElements[1] ?? 128;
                $serverIPBin      = $this->ip2bin($serverIP);

                // IP类型不符
                if (strlen($realIPBin) !== strlen($serverIPBin)) {
                    continue;
                }

                if (strncmp($realIPBin, $serverIPBin, (int) $serverIPPrefix) === 0) {
                    $this->realIP = $tempIP;
                    break;
                }
            }
        }
    }

    if (!$this->isValidIP($this->realIP)) {
        $this->realIP = '0.0.0.0';
    }

    return $this->realIP;
}

/**
 * 检测是否是合法的IP地址
 *
 * @param string $ip   IP地址
 * @param string $type IP地址类型 (ipv4, ipv6)
 *
 * @return boolean
 */
public function isValidIP(string $ip, string $type = ''): bool
{
    switch (strtolower($type)) {
        case 'ipv4':
            $flag = FILTER_FLAG_IPV4;
            break;
        case 'ipv6':
            $flag = FILTER_FLAG_IPV6;
            break;
        default:
            $flag = null;
            break;
    }

    return boolval(filter_var($ip, FILTER_VALIDATE_IP, $flag));
}