正文
获取用户主机的ip地址,这是个很常见的需求,如何准确获取到呢,下面讲一讲。
我们用的框架是Yii2,就放在这个中讲吧,当然核心原理是完全独立的。
上一个方法:
namespace common\components;
use common\misc\LError;
use common\misc\LUtil;
use Yii;
use yii\web\Request;
class LHttpRequest extends Request
{
public function getUserHostAddress()
{
if (!empty($_SERVER['RAW_REMOTE_ADDR'])) {
$_SERVER["REMOTE_ADDR"] = $_SERVER['RAW_REMOTE_ADDR'];
} else if (isset($_SERVER['HTTP_CDN_SRC_IP'])) {
list($_SERVER["REMOTE_ADDR"]) = explode(',', $_SERVER["HTTP_CDN_SRC_IP"]);
$_SERVER["REMOTE_ADDR"] = trim($_SERVER["REMOTE_ADDR"]);
} else if (!isset($_SERVER['REMOTE_ADDR']) || LUtil::isLAN($_SERVER['REMOTE_ADDR'])) {
if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
list($_SERVER["REMOTE_ADDR"]) = explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"]);
$_SERVER["REMOTE_ADDR"] = trim($_SERVER["REMOTE_ADDR"]);
}
}
return $_SERVER["REMOTE_ADDR"];
}
}
这里继承了Yii的Request类,所以在配置文件中需要重新配置request组件:
"components" => [
'request' => array(
'class' => 'common\components\LHttpRequest',
'enableCookieValidation' => true,
'enableCsrfValidation' => false,
'parsers' => [
'application/json' => 'yii\web\JsonParser',
],
),
'response' => array(
'format'=> yii\web\Response::FORMAT_JSON,
),
'errorHandler' => array(
'class' => 'common\components\LErrorHandler',
),
],
再看一下LUtil::isLAN方法:
namespace common\misc;
class LUtil
{
public static function isLAN( $ip )
{
$ip = ip2long( $ip );
$net_a = ip2long('10.0.0.0') >> 24; //A类网预留ip的网络地址 10.0.0.0 ~ 10.255.255.255
$net_b = ip2long('172.16.0.0') >> 20; //B类网预留ip的网络地址 172.16.0.0 ~ 172.31.255.255
$net_c = ip2long('192.168.0.0') >> 16; //C类网预留ip的网络地址 192.168.0.0 ~ 192.168.255.255
return $ip >> 24 === $net_a || $ip >> 20 === $net_b || $ip >> 16 === $net_c;
}
}
下面开始解说:
$_SERVER["REMOTE_ADDR"]
是当前用户的 IP 地址,都存在,不过可能是经过修改的;
$_SERVER["HTTP_X_FORWARDED_FOR"]
可以获得位于代理(网关)后面的直接IP,当然必须这个代理支持;
当然,上面的并不一定确定是用户的真实IP,只是一种获取方法,完善的方法,需要细究。
我们打印$_SERVER
看一下:
array(32) {
["USER"]=>
string(8) "www-data"
["HOME"]=>
string(8) "/var/www"
["HTTP_UPGRADE_INSECURE_REQUESTS"]=>
string(1) "1"
["HTTP_CONNECTION"]=>
string(10) "keep-alive"
["HTTP_ACCEPT_ENCODING"]=>
string(13) "gzip, deflate"
["HTTP_ACCEPT_LANGUAGE"]=>
string(59) "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"
["HTTP_ACCEPT"]=>
string(63) "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
["HTTP_USER_AGENT"]=>
string(68) "Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0"
["HTTP_HOST"]=>
string(8) "test.com"
["REDIRECT_STATUS"]=>
string(3) "200"
["SERVER_NAME"]=>
string(8) "crm.host"
["SERVER_PORT"]=>
string(2) "80"
["SERVER_ADDR"]=>
string(9) "127.0.0.1"
["REMOTE_PORT"]=>
string(5) "43828"
["REMOTE_ADDR"]=>
string(9) "127.0.0.1"
["SERVER_SOFTWARE"]=>
string(12) "nginx/1.10.3"
["GATEWAY_INTERFACE"]=>
string(7) "CGI/1.1"
["REQUEST_SCHEME"]=>
string(4) "http"
["SERVER_PROTOCOL"]=>
string(8) "HTTP/1.1"
["DOCUMENT_ROOT"]=>
string(32) "/var/www/html/test/web"
["DOCUMENT_URI"]=>
string(10) "/index.php"
["REQUEST_URI"]=>
string(1) "/"
["SCRIPT_NAME"]=>
string(10) "/index.php"
["CONTENT_LENGTH"]=>
string(0) ""
["CONTENT_TYPE"]=>
string(0) ""
["REQUEST_METHOD"]=>
string(3) "GET"
["QUERY_STRING"]=>
string(0) ""
["SCRIPT_FILENAME"]=>
string(42) "/var/www/html/test/web/index.php"
["FCGI_ROLE"]=>
string(9) "RESPONDER"
["PHP_SELF"]=>
string(10) "/index.php"
["REQUEST_TIME_FLOAT"]=>
float(1619666810.484)
["REQUEST_TIME"]=>
int(1619666810)
}
参考资料
HTTP 请求头中的 X-Forwarded-For https://blog.csdn.net/u013982161/article/details/55813860
https://www.jb51.net/article/37690.htm
PHP 位运算符 http://www.php.net/manual/zh/language.operators.bitwise.php
ip2long http://www.php.net/manual/en/function.ip2long.php
long2ip http://php.net/manual/en/function.long2ip.php
$_SERVER http://php.net/manual/zh/reserved.variables.server.php
PHP $_SERVER系统变量解说 https://ibaiyang.github.io/blog/php/2018/08/11/PHP-$_SERVER%E7%B3%BB%E7%BB%9F%E5%8F%98%E9%87%8F%E8%A7%A3%E8%AF%B4.html