Openresty + GeoIP2 实现 IP 归属地查询

为了实现业务系统针对不同地区 IP 访问,展示包含不同地区信息的业务交互界面。很多情况下系统需要根据用户访问的 IP 信息,判断用户可能的访问区域,针对不同的区域提供个性化的服务内容。本方案在 CentOS7.x 环境下基于高性能的 Openresty1.13.6.2 来实现。

方案介绍

要通过 IP 地址确认归属地,通常可以使用一些在线查询服务来实现,比如https://blog.csdn.net/XinTeng2012/article/details/34418117?utm_source=blogxgwz8介绍的常见提供查询服务。 但使用在线服务查询潜在存在性能问题,同时通过 lua 来访问外部服务增加额外的代码量。 通过本地的 GeoIP 库来实现查询是个比较好的方案,GeoIP 提供免费和收费服务(https://www.maxmind.com/en/home),大多数情况下使用定期更新的 GeoIP 数据库能满足基本需求。

因此,可以在 openresty 中通过 lua 库本地 GeopIP 数据库的方式来实现快速位置查询和用户访问界面重定向。

环境准备

一:OpenResty 安装

OpenResty 方便地将 Nginx 和常用的各类 lua 库打包发布,可以方便地参考 https://openresty.org/en/installation.html文档从源码编译安装。主要安装步骤说明如下:

tar -xvf openresty-VERSION.tar.gz
cd openresty-VERSION/
./configure -j2 --prefix=/usr/local/openresty
make -j2
sudo make install
 
# better also add the following line to your ~/.bashrc or ~/.bash_profile file.
export PATH=/usr/local/openresty/bin:$PATH

这里的 VERSION 是 OpenResty 具体版本号,目前为 1.13.6.2。编译安装后可以通过如下命令查看版本信息:

/usr/local/openresty/bin/openresty -V
nginx version: openresty/1.13.6.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)?
built with OpenSSL 1.0.2k-fips ?26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.13 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.15 --add-module=../rds-csv-nginx-module-0.09 --add-module=../ngx_stream_lua-0.0.5 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --with-stream --with-stream_ssl_module --with-http_ssl_module

openresty 包含了自身的包维护工具 opm,该工具采用 perl 实现依赖 MD5,需要执行 yum install -y perl-Digest-MD5 安装。

二:GeoIP2 安装
https://dev.maxmind.com/geoip/geoip2/geolite2/下载 MaxMind 格式的 GeoIP2 数据库保存到本地服务器。比如将数据库文件 GeoLite2-City.mmdb 保存到 /usr/local/openresty 目录下。

GeoIP2 lua 库安装,GeoIP2 lua 库位于https://github.com/anjia0532/lua-resty-maxminddb,可以通过如下命令方便安装:

/usr/local/openresty/bin/opm get anjia0532/lua-resty-maxminddb

GeoIP2 lua 库依赖动态库安装,lua 库依赖 libmaxminddb 实现对 mmdb 的高效访问。需要编译该库并添加到 openresty 访问环境。可以从https://github.com/maxmind/libmaxminddb/releases下载相应源码包到本地编译部署。基本编译步骤如下:

$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig

默认情况下上述操作会将 libmaxminddb.so 部署到 /usr/local/lib 目录下,为了让 openresty 访问,可以拷贝到 openresty 目录下,或通过如下步骤更新 ldconfig。

$ sudo sh -c "echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf"
$ ldconfig

三:配置 openresty nginx 环境。

1,配置 openresty nginx 加载相应的 lua 库和动态库,需要在 http 段添加如下指令,其中的;;表示默认库路径:

lua_package_path  "/usr/local/openresty/lualib/?.lua;;";
lua_package_cpath  "/usr/local/openresty/lualib/?.so;;";

2,指定 lua 处理请求的方式。 为了简易直观,如下示例的 nginx.conf 配置指定 /lua 开始的 url 请求通过 conf/lua/test.lua 脚本来处理,这里没有做其他复杂的请求和变量处理工作。 现网环境下可能需要考虑更好的模块化配置管理方式,lua_code_cache off;参数只为测试使用,生产环境需设为on

http {
    lua_package_path  "/usr/local/openresty/lualib/?.lua;;";
    lua_package_cpath  "/usr/local/openresty/lualib/?.so;;";
 
    include       mime.types;
    default_type  application/octet-stream;
 
    server {
        listen       80;
        server_name  localhost;
 
        location / {
            root   html;
            index  index.html index.htm;
        }
        location /lua {
            default_type "text/html";
            charset utf-8;
            lua_code_cache off;
            content_by_lua_file  conf/lua/test.lua;
        }
    }
}

四:编写访问 mmdb 的 lua 脚本

我们想通过传入 IP 地址的方式来查看归属地城市,比如,http://192.168.137.70/lua?ip=111.47.224.182&node=city。 可以如下方式编写按照上面设置的conf/lua/test.lua

ngx.say("
IP location query result:

") local cjson=require 'cjson' local geo=require 'resty.maxminddb' local arg_ip=ngx.var.arg_ip local arg_node=ngx.var.arg_node ngx.say("IP:",arg_ip,", node:",arg_node,"
") if not geo.initted() then geo.init("/usr/local/openresty/GeoLite2-City.mmdb") end local res,err=geo.lookup(arg_ip or ngx.var.remote_addr) if not res then ngx.say("Please check the ip address you provided:
",arg_ip,"
") ngx.log(ngx.ERR,' failed to lookup by ip , reason :',err) else ngx.say("Result:",cjson.encode(res)) if arg_node then ngx.say("node name:",ngx.var.arg_node, " , value:",cjson.encode(res[ngx.var.arg_node] or {})) end end

五:访问验证

curl -k -L "http://192.168.137.70/lua?ip=111.47.224.182&node=country"

IP location query result:

IP:111.47.224.182, Node:country
Result:{"city":{"geoname_id":1791247,"names":{"en":"Wuhan","ru":"Ухань","fr":"Wuhan","pt-BR":"Wuhan","zh-CN":"武汉","es":"Wuhan","de":"Wuhan","ja":"武漢市"}},"subdivisions":[{"geoname_id":1806949,"names":{"en":"Hubei","ru":"Хубэй","fr":"Province de Hubei","pt-BR":"Hubei","zh-CN":"湖北省","es":"Hubei","de":"Hubei","ja":"湖北省"},"iso_code":"HB"}],"country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中国","es":"China","de":"China","ja":"中国"},"iso_code":"CN"},"registered_country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中国","es":"China","de":"China","ja":"中国"},"iso_code":"CN"},"location":{"time_zone":"Asia\/Shanghai","longitude":114.2734,"accuracy_radius":200,"latitude":30.5801},"continent":{"geoname_id":6255147,"names":{"en":"Asia","ru":"Азия","fr":"Asie","pt-BR":"Ásia","zh-CN":"亚洲","es":"Asia","de":"Asien","ja":"アジア"},"code":"AS"}}
Node name:country
Value:{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中国","es":"China","de":"China","ja":"中国"},"iso_code":"CN"} "武汉"

你可能感兴趣的:(GeoIP2)