获取客户端IP、获取公网IP、验证IP是否内网

获取客户端IP、获取公网IP、验证IP是否内网

1、获取客户端IP

在开发工作中,我们常常需要获取客户端的IP。一般获取客户端的IP地址的方法是:request.getRemoteAddr();但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了。

原因:由于在客户端和服务之间增加了中间代理,因此服务器无法直接拿到客户端的IP,服务器端应用也无法直接通过转发请求的地址返回给客户端。

现在图示代理上网和IP的关系:

第一种情况:不通过代理上网,服务器端拿到真实IP
代理上网和IP的关系

第二种情况:通过代理服务器如:Nginx,Squid等一层代理或多层代理上网,如下图:
多层代理IP

需要注意的是X-Forwarded-For和X-Real-IP都不是http的正式协议头,而是squid等反向代理软件最早引入的,之所以resin能拿到,是因为NGINX里一般缺省都会这么配置转发的http请求:

location / {

         proxy_pass       http://yourdomain.com;

         proxy_set_header   Host             $host;

         proxy_set_header   X-Real-IP        $remote_addr;

         proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

         }

从X-Forwarded-For的定义来看,ips[0]才是原始客户端ip,如果这个都不是,那拿第二个就更不靠谱了,我们平时检验的时候,可能是直接在内网挂代理去访问的,跟外面网友访问经过的网络路径不一样,后面不停添加的是经过的每一层代理ip才对,下面举例说明;

request.getRemoteAddr() 192.168.239.196

request.getHeader("X-Forwarded-For") 58.63.227.162, 192.168.237.178,192.168.238.218

request.getHeader("X-Real-IP") 192.168.238.218

所以访问的流程应该是这样,客户端58.63.227.162发出请求,经过192.168.237.178, 192.168.238.218两层转发,到了192.168.239.196这台NGINX上,NGINX就把X-Real-IP头设成了自己看到的remote_addr,也就是直接发给到他的192.168.238.218,这时候resin收到这个包,对resin来说直接发给他的remote_addr就是NGINX的ip,也就是192.168.239.196,那么resin里面的request.getRemoteAddr()就是192.168.239.196,那么在resin里拿最原始的ip逻辑(也就是拿能够知道的最外层的ip)应该是这样:

如果XFF不为空,拿XFF的左边第一个

如果XFF为空,拿XRI

如果XRI为空,只能拿request.getRemoteAddr(),也就是只能拿到最直接发给他的机器ip了

其他都不可考究,参考代码如下:

第一种代码:

    /**
     * 从Request对象中获得客户端IP,处理了HTTP代理服务器和Nginx的反向代理截取了ip
     * 
     * @param request
     * @return ip
     */
    public static String getRealIp1(HttpServletRequest request) {
        String remoteAddr = request.getRemoteAddr();
        String forwarded = request.getHeader("X-Forwarded-For");
        String realIp = request.getHeader("X-Real-IP");

        String ip = null;
        if (realIp == null) {
            if (forwarded == null) {
                ip = remoteAddr;
            } else {
                ip = remoteAddr + "/" + forwarded.split(",")[0];
            }
        } else {
            if (realIp.equals(forwarded)) {
                ip = realIp;
            } else {
                if (forwarded != null) {
                    forwarded = forwarded.split(",")[0];
                }
                ip = realIp + "/" + forwarded;
            }
        }
        return ip;
    }

第二种代码:

     /**
     * 从Request对象中获得客户端IP,处理了HTTP代理服务器和Nginx的反向代理截取了ip
     * @param request
     * @return ip
     */
    public static String getRealIp2(HttpServletRequest request) {
        String remoteAddr = request.getRemoteAddr();
        String forwarded = request.getHeader("X-Forwarded-For");
        String realIp = request.getHeader("X-Real-IP");

        String ip = null;
        if (realIp == null) {
            if (forwarded == null) {
                ip = remoteAddr;
            } else {
                ip = remoteAddr + "/" + forwarded;
            }
        } else {
            if (realIp.equals(forwarded)) {
                ip = realIp;
            } else {
                ip = realIp + "/" + forwarded.replaceAll(", " + realIp, "");
            }
        }
        return ip;
    }

第三种代码:

     /**
     * 从Request对象中获得客户端IP,处理了HTTP代理服务器和Nginx的反向代理截取了ip
     * @param request
     * @return ip
     */
    public static String getRealIp3(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
    }

第三种是最合适的,最清晰理解的

2、获取公网IP

    /**
     * Java获取本机公网ip
     * @return ip
     * @throws SocketException
     */
    public static String getRealIp() throws SocketException {

        String localip = null;// 本地IP,如果没有配置外网IP则返回它
        String netip = null;// 外网IP
        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP

        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
                    netip = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
                    localip = ip.getHostAddress();
                }
            }
        }

        if (netip != null && !"".equals(netip)) {
            return netip;
        } else {
            return localip;
        }
    }

3、验证IP是否内网

tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下:

10.0.0.0/8:10.0.0.0~10.255.255.255
172.16.0.0/12:172.16.0.0~172.31.255.255
192.168.0.0/16:192.168.0.0~192.168.255.255

那么,直接上代码吧:

    /**
     * Java判断IP地址为内网IP还是公网IP
     * @param ip
     * @return boolean
     */
    public static boolean internalIp(String ip) {
         byte[] addr = IPAddressUtil.textToNumericFormatV4(ip);
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        //10.x.x.x/8
        final byte SECTION_1 = (byte) 0x0A;
        //172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        //192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }
### Linux系统中配置网卡绑定公网IP #### 配置网络接口文件 为了使私有云中的虚拟机能够通过iptables和frp映射到公网使用,在Linux环境中,通常需要编辑网络接口配置文件来设置静态IP地址或其他必要的参数。对于Red Hat系发行版而言,这涉及到修改`/etc/sysconfig/network-scripts/ifcfg-eth0-range0`这样的路径下的相应文件[^2]。 ```bash DEVICE=eth0 BOOTPROTO=static ONBOOT=yes TYPE=Ethernet USERCTL=no PEERDNS=yes NM_CONTROLLED=yes IPADDR=192.168.1.100 # 这里填写分配给该网卡的内网或外网固定IP地址 NETMASK=255.255.255.0 GATEWAY=192.168.1.1 # 默认网关地址 DNS1=8.8.8.8 # 可选:指定首选DNS服务器 ``` 上述脚本展示了如何定义一个具有特定IP地址、子网掩码、默认网关及可选项如DNS服务器的基础网络接口配置。请注意替换示例中的IP地址为实际环境所需使用的数值。 #### 使用FRP进行端口转发 为了让位于私有网络内的服务可以通过互联网访问,可以利用Fast Reverse Proxy (FRP)工具完成从外部世界至内部资源的安全穿透操作。只需在Frp客户端配置文件(`frpc.ini`)中指明目标机器上的监听地址与端口号,以及希望暴露于公网上对应的入口位置即可[^1]: ```ini [common] server_addr = x.x.x.x ; FRPS服务器所在的公网IP server_port = 7000 ; FRPS监听的服务端口 [tcp_service] type = tcp ; 协议类型 local_ip = 192.168.1.100 ; 内部VPS实例的实际IP local_port = 22 ; VPS上运行的应用程序所占用本地端口 remote_port = 6000 ; 对应公网开放供外界连接的远端端口 ``` 此段配置实现了SSH服务(假设其工作于标准TCP 22号端口之上)经由FRP代理后被映射到了公共Internet空间下某处固定的6000号端口;因此任何尝试建立ssh会话的人都可以直接指向<http://x.x.x.x>:6000来进行通信。 #### 查看当前网络状态 当完成了以上两步之后,可能还需要确认系统的网络状况是否正常运作。此时便可以用netstat命令加上不同的标志位来获取有关活动链接的信息摘要表单[^3]: ```bash netstat -atunlp | grep LISTEN ``` 这条指令将会筛选并展示所有处于等待接收新请求的状态下的套接字记录,并附带它们关联着哪个进程及其ID编号。这对于验证之前所做的更改是否有误非常有用——比如检查新开辟出来的端口是不是真的已经准备就绪接受来自远方的数据包了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值