{"msg":"操作成功","code":200,"data":{"createBy":"admin","createTime":"2023-05-27 10:42:01","updateBy":"admin","updateTime":"2025-06-18 21:32:42","remark":null,"params":{"@type":"java.util.HashMap"},"id":76,"articleTitle":"Nginx（四）获取客户端IP","articleUrl":"nginx_x_forward_for","articleThumbnail":"https://www.asumimoe.com/imgfiles/20250618/4b57c2e46f8949e5a30d4e2e9e8a8fd5.png","articleFlag":"0","draftStatus":"1","reprintStatement":"1","articleSummary":"通常系统出于安全考虑，需要进行权限(账号、密码)和IP黑白名单控制。这就需要获取访问来源真实的IP，如果系统设置nginx代理等操作，会使你达到目的不那么简单直接。","articleContent":"通常系统出于安全考虑，需要进行权限(账号、密码)和IP黑白名单控制。这就需要获取访问来源真实的IP，如果系统设置nginx代理等操作，会使你达到目的不那么简单直接。\n\n## 配置nginx转发真实客户端IP\n\n在nginx中为我们配置了一些默认变量。\n\n```shell\n$remote_addr # 来自对端socket的ip地址\n$remote_port # 来自对端socket的port信息\n$proxy_add_x_forwarded_for # http/https请求流经的所有代理IP\n```\n\n直接通过`$remote_addr`获取的是与web服务器建立TCP连接的ip地址信息。如果用户直接访问web服务器，那么我们获取的就是用户的真实IP，如果用户通过代理服务器访问web服务器那么我们获取的就是代理服务器的IP。\n\n若要获取Nginx代理前的客户端真实IP，则需要在nginx.conf配置文件做如下修改：修改server→location的配置。\n\n```shell\nproxy_set_header X-Real-IP $remote_addr; #保留代理之前的真实客户端ip\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#记录代理过程\n\nserver {\nlisten   7777;\nserver_name  192.168.10.8:7777;\nlocation /unstructured/ {\n    proxy_pass http://192.168.10.8:8080/unstructured/;\n    proxy_set_header  Host $host;\n    proxy_set_header    X-Real-IP        $remote_addr;\n    proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;\n    }\n}\n```\n\n由于nginx是7层代理, 不是4层代理。7层代理的意思我们只能从修改第七层的包信息，因此不可能在tcp/ip这层做手脚. 只能在http/https这个应用层协议中做文章。nginx的策略是: 往http/https请求中, 添加额外的header信息, 以此来完成真实客户端ip的信息传递。\n\n在上面的例子中，如果只有一层nginx代理，那么通过`X-Real-IP`与`X-Forwarded-For`都可获得客户端真实IP，如果经过了多级代理呢？\n\n`X-Forwarded-For`获取的IP代表客户端，也就是HTTP的请求端真实的IP，只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项（没有经过的获取为空），标准格式如下：`X-Forwarded-For: client1, proxy1, proxy2`。\n\n从标准格式可以看出，X-Forwarded-For头信息可以有多个，中间用逗号分隔，第一项为真实的客户端ip，剩下的就是曾经经过的代理或负载均衡的ip地址，经过几个就会出现几个。\n\n## Tomcat获取真实IP\n\n修改server.xml配置文件来使tomcat获取客户端真实IP\n\n```xml\n<!--方案一：修改 Valve className=\"org.apache.catalina.valves.AccessLogValve\" pattern中的配置%h为%{X-Forwarded-For}i-->\n<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs\" prefix=\"localhost_access_log\" suffix=\".txt\" pattern=\"%{X-Real-IP}i %l %u %t \"%r\" %s %b\" />\n\n<!--方案二：不修改pattern，增加Valve className=\"org.apache.catalina.valves.RemoteIpValve\"-->\n<Valve className=\"org.apache.catalina.valves.RemoteIpValve\" remoteIpHeader=\"x-forwarded-for\" remoteIpProxiesHeader=\"x-forwarded-by\" />\n```\n\n## JAVA项目获取真实IP\n\n```java\npublic static String getLocalIp(HttpServletRequest request) {\n    String remoteAddr = request.getRemoteAddr();//默认获取客户端IP，无nginx代理情况\n    String forwarded = request.getHeader(\"X-Forwarded-For\");//有nginx代理，且设置了X-Forwarded-For\n    String realIp = request.getHeader(\"X-Real-IP\");//一级代理，则此值与forwarded一致\n\n    if (!Strings.isNullOrEmpty(forwarded) && !\"unKnown\".equalsIgnoreCase(forwarded)) {\n        //多次反向代理后会有多个ip值，第一个ip才是真实ip\n        int index = forwarded.indexOf(\",\");\n        if (index != -1) {\n            return forwarded.substring(0, index);\n        } else {\n            return forwarded;\n        }\n    }\n}\n```","categoryId":12,"viewCount":535,"categoryName":"Nginx","author":"球接子","authorAvatar":null,"tagIds":[13,14],"tagNames":["Nginx","中间件"]}}