1987WEB视界-分享互联网热点话题和事件

您现在的位置是:首页 > 域名 > 正文

域名

nginx获取客户端真实ip、域名、协议和端口

1987web2022-09-13域名961

nginx是一种反向代理和负载均衡服务器,它不仅很轻量,还拥有高性能特性和支持插件化开发。使用者可以根据自身需求为nginx指定某款插件来增强nginx在某种特定场景下的功能或提升nginx在某种特定场景下的性能。

nginx反向代理后,servlet应用通过request.getRemoteAddr可以获取到ip(nginx的ip地址,并非真实的客户端ip地址),通过request.getRequestURL方法获取到的域名也都是nginx访问应用时的域名、协议和端口,并非真实客户端的域名、协议和端口等。

直接获取信息有哪些问题?

如下配置,本地服务器的jetty或者tomcat端口为808,nginx端口号为80,nginx反向代理8080端口

server{listen80;location/ {proxy_passhttp://127.0.0.1:8080;反向代理应用服务器HTTP地址}}

另外一个机器B访问http://192.168.1.6/test访问某个Servlet应用,获取到客户端ip和URL

System.out.println("RemoteAddr: " +request.getRemoteAddr());System.out.println("URL: " +request.getRequestURL().toString());

运行的结果信息为

RemoteAddr: 127.0.0.1URL: http://127.0.0.1:8080/test

通过Servlet获取到的客户端ip是nginx的ip并非机器B的ip,获取到的url是nginx配置的proxy_passs的URL地址,而非机器B浏览器上的真实地址。如果nginx作为https反向代理到后端的http服务,那么request.getRequestURL获取到URL是http前缀而非https前缀,无法获取到真实协议。如果借助request.getRequestURL获取的url用为拼接跳转地址,就会跳转到错误地址,这是nginx反向代理经常发生的问题。

如何解决这种问题?

解决问题的思考方向:1.nginx是代理服务器,客户端请求经nginx转发到jetty/tomcat,如果Nginx不把客服端真实的ip、域名、端口、协议告诉jetty/tomcat,那么jetty/tomcat将无法知道,因此要配置http header将这些信息告诉jetty/tomcat。2.jetty/tomcat不能直接获取连接它的客户端(nginx)信息,而是要从http header获取客户端的信息。

proxy_set_headerHost$http_host;proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_headerX-Forwarded-Proto$scheme;

参数含义:

host:包含客户端真实的域名和端口信息

X-Real-IP:客户端真实的ip

X-Forwarded-For:这个和X-Real-IP类似,但它在多层代理时会包含真实客户端和中间每个代理服务器的ip

X-Forwarded-Proto:客户端的协议信息

nginx配置完之后,重启运行程序

RemoteAddr: 127.0.0.1URL: http://192.168.1.6/test

我们发现获取到的url是对的,而ip还是非真实的。如果用nginx作为https服务器反向代理到http服务器,会发现浏览器地址是https前缀但是通过request.getRequestURL获取到的URL还是http前缀,说明单靠nginx配置还不能解决问题。

通过java获取客户端的信息

通过如下方式能够获取到客户端的真实ip,因为servlet api提供了request.getRemoteAddr方法获取客户端ip,所以不管是否使用反向代理对于开发者应该是透明的。

/**** 获取客户端IP地址;这里通过了Nginx获取;X-Real-IP*/publicstaticStringgetClientIP(HttpServletRequest request) {StringfromSource ="X-Real-IP";Stringip = request.getHeader("X-Real-IP");if(ip ==null|| ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Forwarded-For");fromSource ="X-Forwarded-For";}if(ip ==null|| ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");fromSource ="Proxy-Client-IP";}if(ip ==null|| ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");fromSource ="WL-Proxy-Client-IP";}if(ip ==null|| ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();fromSource ="request.getRemoteAddr";}returnip;}

jetty服务器

在jetty.xml配置文件中找到httpConfig进行配置

<Newid="httpConfig"class="org.eclipse.jetty.server.HttpConfiguration">...<Callname="addCustomizer"><Arg><Newclass="org.eclipse.jetty.server.ForwardedRequestCustomizer"/>Arg>Call>New>

重启jetty服务器,再次访问http://192.168.1.6/test,结果如下

RemoteAddr: 192.168.1.6URL: http://192.168.1.6/test

通过request.getRemoteAddr获取到的ip就是客户端真实ip,获取到的URL也是真实的URL,nginx作为https代理,获取到的协议也会是https。如果不想改jetty.xml配置文件,jetty提供了http-forwarder模块,所以可以直接通过命令行启动

java -jar demo.jar --module=http-forwarded

tomcat

在tomcat的server.xml配置文件的host元素配置

<ValveclassName="org.apache.catalina.valves.RemoteIpValve"/>