openresty開發系列40--nginx+lua實現獲取客戶端ip所在的國家信息 為了實現業務系統針對不同地區IP訪問,展示包含不同地區信息的業務交互界面。很多情況下系統需要根據用戶訪問的IP信息,判斷用戶可能的訪問區域,針對不同的區域提供個性化的服務內容。本方案在CentOS7.6環境下基于高性能的Openresty1.13.6.1來實現。 方案介紹 要通過IP地址確認歸屬地,通常可以使用一些在線查詢服務來實現,但使用在線服務查詢潛在存在性能問題,同時通過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 # vim /etc/profile export PATH=/usr/local/openresty/bin:$PATH 這里的VERSION 是OpenResty具體版本號,目前為 1.13.6.1,編譯安裝后可以通過如下命令查看版本信息: [root@node5 conf]# /usr/local/openresty/bin/openresty -V nginx version: openresty/1.13.6.1 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (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.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.07 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.11 --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.18 --add-module=../redis2-nginx-module-0.14 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.15 --add-module=../rds-csv-nginx-module-0.08 --add-module=../ngx_stream_lua-0.0.3 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --with-pcre --with-http_gzip_static_module --with-http_realip_module --with-http_geoip_module --with-http_ssl_module --with-http_stub_status_module --with-stream --with-stream_ssl_module openresty包含了自身的包維護工具opm,該工具采用perl實現依賴MD5,需要執行yum install? -y perl-Digest-MD5 安裝 二:GeoIP2安裝 1.從https://dev.maxmind.com/geoip/geoip2/geolite2/ 下載MaxMind格式的GeoIP2數據庫保存到本地服務器,將數據庫文件GeoLite2-City.mmdb保存到/usr/local/openresty目錄下 2.GeoIP2 lua庫安裝,GeoIP2 lua庫位于https://github.com/anjia0532/lua-resty-maxminddb ,可以通過如下命令方便安裝: # /usr/local/openresty/bin/opm get anjia0532/lua-resty-maxminddb 3.GeoIP2 lua庫依賴動態庫安裝:lua庫依賴libmaxminddb實現對mmdb的高效訪問。需要編譯該庫并添加到openresty訪問環境。 從https://github.com/maxmind/libmaxminddb/releases下載相應源碼包到本地編譯部署 基本編譯步驟如下: tar xf libmaxminddb-1.3.2.tar.gz cd libmaxminddb-1.3.2 ./configure make make check make install ldconfig 默認情況下上述操作會將libmaxminddb.so部署到/usr/local/lib目錄下,為了讓openresty訪問,可以拷貝到openresty目錄下,或通過如下步驟更新ldconfig。 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配置指定 /ipinfo 開始的url請求通過/usr/local/lua/getipinfo.lua腳本來處理,這里沒有做其他復雜的請求和變量處理工作。 lua_code_cache off;參數只為測試使用,生產環境需設為on; nginx.conf的server部分添加如下location: ?? ?location /ipinfo { ??????????????? default_type "text/html"; ??????????????? charset utf-8; ??????????????? content_by_lua_file? /usr/local/lua/getipinfo.lua; ??????? } # 獲取ip歸屬的lua腳本:
# vim /usr/
local /lua/
getipinfo.luangx.say( " <br>IP location query result:<hr><br> " ) 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,
" <br> " ) 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: <div style='color:red'> " ,arg_ip,
" </div> " )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 訪問接口: http://10.11.0.215/ipinfo?ip=120.76.101.211&node=city IP location query result: IP:120.76.101.211, node:city Result:{"city":{"geoname_id":1808926,"names":{"en":"Hangzhou","ru":"Ханчжоу","fr":"Hangzhou","pt-BR":"Hangzhou","zh-CN":"杭州","es":"Hangzhou","de":"Hangzhou","ja":"杭州市"}},"subdivisions":[{"geoname_id":1784764,"names":{"en":"Zhejiang","fr":"Province de Zhejiang","zh-CN":"浙江省"},"iso_code":"ZJ"}],"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":120.1619,"accuracy_radius":50,"latitude":30.294},"continent":{"geoname_id":6255147,"names":{"en":"Asia","ru":"Азия","fr":"Asie","pt-BR":"ásia","zh-CN":"亞洲","es":"Asia","de":"Asien","ja":"アジア"},"code":"AS"}} node name:city , value:{"geoname_id":1808926,"names":{"en":"Hangzhou","ru":"Ханчжоу","fr":"Hangzhou","pt-BR":"Hangzhou","zh-CN":"杭州","es":"Hangzhou","de":"Hangzhou","ja":"杭州市"}} 格式化輸出: { ?? ?"city": { ?? ??? ?"geoname_id": 1808926, ?? ??? ?"names": { ?? ??? ??? ?"en": "Hangzhou", ?? ??? ??? ?"ru": "Ханчжоу", ?? ??? ??? ?"fr": "Hangzhou", ?? ??? ??? ?"pt-BR": "Hangzhou", ?? ??? ??? ?"zh-CN": "杭州", ?? ??? ??? ?"es": "Hangzhou", ?? ??? ??? ?"de": "Hangzhou", ?? ??? ??? ?"ja": "杭州市" ?? ??? ?} ?? ?}, ?? ?"subdivisions": [{ ?? ??? ?"geoname_id": 1784764, ?? ??? ?"names": { ?? ??? ??? ?"en": "Zhejiang", ?? ??? ??? ?"fr": "Province de Zhejiang", ?? ??? ??? ?"zh-CN": "浙江省" ?? ??? ?}, ?? ??? ?"iso_code": "ZJ" ?? ?}], ?? ?"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": 120.1619, ?? ??? ?"accuracy_radius": 50, ?? ??? ?"latitude": 30.294 ?? ?}, ?? ?"continent": { ?? ??? ?"geoname_id": 6255147, ?? ??? ?"names": { ?? ??? ??? ?"en": "Asia", ?? ??? ??? ?"ru": "Азия", ?? ??? ??? ?"fr": "Asie", ?? ??? ??? ?"pt-BR": "ásia", ?? ??? ??? ?"zh-CN": "亞洲", ?? ??? ??? ?"es": "Asia", ?? ??? ??? ?"de": "Asien", ?? ??? ??? ?"ja": "アジア" ?? ??? ?}, ?? ??? ?"code": "AS" ?? ?} } node name: city, value: { ?? ?"geoname_id": 1808926, ?? ?"names": { ?? ??? ?"en": "Hangzhou", ?? ??? ?"ru": "Ханчжоу", ?? ??? ?"fr": "Hangzhou", ?? ??? ?"pt-BR": "Hangzhou", ?? ??? ?"zh-CN": "杭州", ?? ??? ?"es": "Hangzhou", ?? ??? ?"de": "Hangzhou", ?? ??? ?"ja": "杭州市" ?? ?} }
線上環境獲取客戶端ip所在國家的示例: nginx.conf主配置,引入ip庫 [root@gdpr04:~]# cat /usr/local/nginx/conf//nginx.conf
#user apache;
worker_processes 8 ;#error_log logs /
error.log;
#error_log logs /
error.log notice;
#error_log logs /error.log
info ;#pid logs /
nginx.pid;
pid /data/www/logs/
nginx.pid;worker_rlimit_nofile 65535 ;events {use epoll; worker_connections 10240 ;
}http {include mime.types;default_type application /octet-
stream;#set_real_ip_from 0.0 .
0.0 /
0 ;#real_ip_header X -Forwarded-
For;#proxy_set_header Host $host; #proxy_set_header X -Real-
IP $remote_addr; #proxy_set_header X -Forwarded-
For $http_x_forwarded_for; #proxy_set_header X -Forwarded-
For $proxy_add_x_forwarded_for; proxy_headers_hash_max_size 51200 ;proxy_headers_hash_bucket_size 6400 ;ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;# fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=
1 :
2 keys_zone=TEST:10m inactive=
5m; fastcgi_connect_timeout 300 ; fastcgi_send_timeout 300 ; fastcgi_read_timeout 300 ; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; # fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; # fastcgi_cache TEST; #fastcgi_cache_valid 200 302 1h; # fastcgi_cache_valid 301 1d; #fastcgi_cache_valid any 1m; # fastcgi_cache_min_uses 1 ;#geoip_country /usr/local/nginx/conf/
GeoIP.dat;#fastcgi_param GEOIP_COUNTRY_CODE $geoip_country_code;geoip2 conf /GeoIP2/GeoIP2-
Country.mmdb {auto_reload 5m;$geoip2_metadata_country_build metadata build_epoch;$geoip2_data_country_code source =
$remote_addr country iso_code;$geoip2_data_country_name country names en;}geoip2 conf /GeoIP2/GeoIP2-
City.mmdb {$geoip2_data_city_name city names en;}fastcgi_param COUNTRY_CODE $geoip2_data_country_code;fastcgi_param COUNTRY_NAME $geoip2_data_country_name;fastcgi_param CITY_NAME $geoip2_data_city_name;open_file_cache max =
204800 inactive=
20s;open_file_cache_min_uses 1 ;open_file_cache_valid 30s; #log_format main ' $remote_addr - $remote_user [$time_local] "$request" ' # ' $status $body_bytes_sent "$http_referer" ' # ' "$http_user_agent" "$http_x_forwarded_for" ' ;# log_format main ' [$time_local] $remote_addr $status $request_time $body_bytes_sent "$request" "$http_referer" $upstream_addr $http_x_real_ip $http_x_forwarded_for $http_user_agent $request_filename ' ;log_format main ' $remote_addr - - [$time_local] - - "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time ' ; # log_format test ' [$fastcgi_script_name] [$time_local] $remote_addr $status $request_time $body_bytes_sent "$request" "$http_referer" $upstream_addr $http_x_real_ip $http_x_forwarded_for $http_user_agent ' ;log_format error ' $remote_addr - - [$time_local] - - "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time ' ; #access_log logs /
access.log main;sendfile on;tcp_nodelay on;keepalive_timeout 70 ;# ----
for upload
file client_max_body_size 8M;client_body_buffer_size 2M;# ---
for resolve
400 errorclient_header_buffer_size 64k;large_client_header_buffers 4 64k;proxy_connect_timeout 60s;proxy_read_timeout 60s;#60s內后端服務器需要返回成功proxy_send_timeout 60s; proxy_buffer_size 16k;proxy_buffers 4 32k;proxy_busy_buffers_size 64k;proxy_temp_file_write_size 64k; gzip on;gzip_vary off;gzip_min_length 1k;gzip_buffers 4 16k;gzip_http_version 1.0 ;gzip_comp_level 3 ;gzip_disable " MSIE [1-6]\. " ;gzip_types text /plain text/css text/javascript application/x-javascript text/xml application/
xml;fastcgi_intercept_errors on;ssi on;ssi_silent_errors on;#ssi_types text /
shtml;expires 30d;server_names_hash_bucket_size 20480 ;#if_modified_since before;#limit_req_zone $binary_remote_addr zone =all_zone:10m rate=3r/
s;#limit_req zone =all_zone burst=
2 nodelay;limit_req_zone $binary_remote_addr $host $request_uri zone =all_zone:30m rate=4r/
s;geo $white_ip {ranges;default 0 ; 1.1 .
1.1 -
1.1 .
1.254 1 ; 192.168 .
254.1 -
192.168 .
254.254 2 ;}limit_req_whitelist geo_var_name =white_ip geo_var_value=
1 ;limit_req_whitelist geo_var_name =white_ip geo_var_value=
2 ;limit_req_whitelist geo_var_name =white_ip geo_var_value=
3 ;limit_req_whitelist geo_var_name =white_ip geo_var_value=
4 ;limit_req_whitelist geo_var_name =white_ip geo_var_value=
5 ;limit_req_whitelist geo_var_name =white_ip geo_var_value=
6 ;# upstream php_pool{# ip_hash;# server unix: /tmp/php-
cgi.sock;# server 192.168 .
254.126 :
9000 max_fails=
0 fail_timeout=30s weight=
3 ;# keepalive 32 ;# keepalive_timeout 30s;# check interval =
3000 rise=
2 fall=
5 timeout=
1000 type=tcp port=
9000 ;# check_keepalive_requests 100 ;# check_http_send " HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n " ;# check_http_expect_alive http_2xx http_3xx;# }include vhost.d /* .conf;server {listen 80 default_server;server_name localhost;location / {root /data/www/html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}location /ws_status {stub_status on;access_log off;}}
} # 具體vhost的配置
# cat country-
info .chinasoft.com.conf
server {listen 80 ;server_name country -
info .chinasoft.com ;#access_log /data/www/logs/nginx_log/access/country-
info .chinasoft.com_access.log main ;#error_log /data/www/logs/nginx_log/error/country-
info .chinasoft.com_error.log ;root /data/www/vhosts/common-
info .chinasoft.com/
httpdocs ;index index.html index.shtml index.php ;error_page 404 403 /
404 .html;location /api/v1/
checkeu {add_header ' Access-Control-Allow-Origin ' ' * ' ;add_header ' Access-Control-Allow-Credentials ' ' true ' ;add_header ' Access-Control-Allow-Methods ' ' GET, POST, OPTIONS ' ;add_header ' Access-Control-Allow-Headers ' ' DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type ' ;default_type ' text/plain ' ;content_by_lua_file ' /usr/local/nginx/conf/vhost.d/checkeu.lua ' ;}}server {listen 443 ;ssl on;ssl_certificate /usr/local/nginx/conf/cert2016/
iskysoft_com.crt; ssl_certificate_key /usr/local/nginx/conf/cert2016/
iskysoft_com.key;ssl_session_timeout 5m;ssl_protocols TLSv1 TLSv1. 1 TLSv1.
2 ;ssl_ciphers " ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!AES128-GCM-SHA256:!AES256-GCM-SHA384:!AES128-SHA256:!AES256-SHA256:!AES128-SHA:!AES256-SHA:AES:!CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA " ;ssl_prefer_server_ciphers on;server_name country -
info .chinasoft.com;access_log /data/www/logs/nginx_log/access/country-
info .chinasoft.com_access.log main ;error_log /data/www/logs/nginx_log/error/country-
info .chinasoft.com_error.log ;root /data/www/vhosts/common-
info .chinasoft.com/
httpdocs ;index index.html ;error_page 404 403 /
404 .html;location /api/v1/
checkeu {add_header ' Access-Control-Allow-Origin ' ' * ' ;add_header ' Access-Control-Allow-Credentials ' ' true ' ;add_header ' Access-Control-Allow-Methods ' ' GET, POST, OPTIONS ' ;add_header ' Access-Control-Allow-Headers ' ' DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type ' ;default_type ' text/plain ' ;content_by_lua_file ' /usr/local/nginx/conf/vhost.d/checkeu.lua ' ;}
} # 獲取國家的lua腳本 # cat /usr/local/nginx/conf/vhost.d/checkeu.lua
--ngx.say(
" {\"c_type\":0} " )
local ngxmatch =
ngx.re.match
usercountry =
ngx.var.geoip2_data_country_code
--usercountry =
ngx.var.geoip_country_code
eopcountry =
" AT|BE|BG|CY|HR|CZ|DK|EE|FI|FR|DE|GR|HU|IE|IT|LV|LT|LU|MT|NL|PL|PT|RO|SK|SI|ES|SE|GB "
if not usercountry
then usercountry =
''
end if not usercity
then usercity =
''
end
if ngxmatch(usercountry,eopcountry,
" isjo " )
then ngx.say( " {\"c_type\":1,\"country_code\":\" " ..usercountry..
" \"} " )
else ngx.say( " {\"c_type\":0,\"country_code\":\" " ..usercountry..
" \"} " )
end 訪問: http://common-info.chinasoft.com/api/v1/checkeu 返回: ?{"c_type":0} {"c_type":0,"country_code":"CN"}
轉載于:https://www.cnblogs.com/reblue520/p/11459250.html
總結
以上是生活随笔 為你收集整理的openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。