网站中取访问用户真实ip地址

本文介绍了一种在JSP环境中获取客户端真实IP地址的方法,尤其是在经过反向代理的情况下的处理方式。通过检查X-Forwarded-For头部信息并考虑多级代理情况来确保获取到正确的IP。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。但是在通过了Apache,Squid,nginx等反向代理软件就不能获取到客户端的真实IP地址了。


        如果使用了反向代理软件,将http://192.168.1.110:2046/ 的URL反向代理为 http://www.java.com.cn / 的URL时,用request.getRemoteAddr()方法获取的IP地址是:127.0.0.1 或 192.168.1.110,而并不是客户端的真实IP。

       经过代理以后,由于在客户端和服务之间增加了中间层,因此服务器无法直接拿到客户端的IP,服务器端应用也无法直接通过转发请求的地址返回给客户端。但是在转发请求的HTTP头信息中,增加了X-FORWARDED-FOR信息。用以跟踪原有的客户端IP地址和原来客户端请求的服务器地址。当我们访问http://www.javapeixun.com.cn /index.jsp/ 时,其实并不是我们浏览器真正访问到了服务器上的index.jsp文件,而是先由代理服务器去访问http://192.168.1.110:2046/index.jsp ,代理服务器再将访问到的结果返回给我们的浏览器,因为是代理服务器去访问index.jsp的,所以index.jsp中通过request.getRemoteAddr()的方法获取的IP实际上是代理服务器的地址,并不是客户端的IP地址。

于是可得出获得客户端真实IP地址的方法一:

public String getRemortIP(HttpServletRequest request) { 
  if (request.getHeader("x-forwarded-for") == null) { 
    return request.getRemoteAddr(); 
  } 
  return request.getHeader("x-forwarded-for"); 
}

    可是当我访问http://www.5a520.cn /index.jsp/ 时,返回的IP地址始终是unknown,也并不是如上所示的127.0.0.1 或 192.168.1.110了,而我访问http://192.168.1.110:2046/index.jsp 时,则能返回客户端的真实IP地址,写了个方法去验证。原因出在了Squid上。squid.conf 的配制文件 forwarded_for 项默认是为on,如果 forwarded_for 设成了 off  则:X-Forwarded-For: unknown

   于是可得出获得客户端真实IP地址的方法二:

   
import javax.servlet.http.HttpServletRequest;  
  
/** 
 * 自定义访问对象工具类 
 *  
 * 获取对象的IP地址等信息 
 * @author X-rapido 
 * 
 */  
public class CusAccessObjectUtil {  
  
    /** 
     * 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, 
     * 参考文章: http://developer.51cto.com/art/201111/305181.htm 
     *  
     * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢? 
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 
     *  
     * 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, 
     * 192.168.1.100 
     *  
     * 用户真实IP为: 192.168.1.110 
     *  
     * @param request 
     * @return 
     */  
    public static String getIpAddress(HttpServletRequest request) {  
        String ip = request.getHeader("x-forwarded-for");  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("Proxy-Client-IP");  
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("WL-Proxy-Client-IP");  
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("HTTP_CLIENT_IP");  
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");  
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getRemoteAddr();  
        }  
        return ip;  
    }  
      
}
 

     可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串Ip值,究竟哪个才是真正的用户端的真实IP呢?

答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。

如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100用户真实IP为: 192.168.1.110

除了上面的简单获取IP之外,一般的公司还会进行内网外网判断,完整示例如下:


  

import javax.servlet.http.HttpServletRequest;

/**
 * IP地址工具类
 * @author xudongdong
 *
 */
public class IpUtil {
    
    /**
     * 私有化构造器
     */
    private IpUtil() {
    }

    /**
     * 获取真实IP地址
     * <p>使用getRealIP代替该方法</p>
     * @param request req
     * @return ip
     */
    @Deprecated
    public static String getClinetIpByReq(HttpServletRequest request) {
        // 获取客户端ip地址
        String clientIp = request.getHeader("x-forwarded-for");

        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("WL-Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getRemoteAddr();
        }
        /*
         * 对于获取到多ip的情况下,找到公网ip.
         */
        String sIP = null;
        if (clientIp != null && !clientIp.contains("unknown") && clientIp.indexOf(",") > 0) {
            String[] ipsz = clientIp.split(",");
            for (String anIpsz : ipsz) {
                if (!isInnerIP(anIpsz.trim())) {
                    sIP = anIpsz.trim();
                    break;
                }
            }
            /*
             * 如果多ip都是内网ip,则取第一个ip.
             */
            if (null == sIP) {
                sIP = ipsz[0].trim();
            }
            clientIp = sIP;
        }
        if (clientIp != null && clientIp.contains("unknown")){
            clientIp =clientIp.replaceAll("unknown,", "");
            clientIp = clientIp.trim();
        }
        if ("".equals(clientIp) || null == clientIp){
            clientIp = "127.0.0.1";
        }
        return clientIp;
    }
    
    /**
     * 判断IP是否是内网地址
     * @param ipAddress ip地址
     * @return 是否是内网地址
     */
    public static boolean isInnerIP(String ipAddress) {
        boolean isInnerIp;
        long ipNum = getIpNum(ipAddress);
        /**   
        私有IP:A类  10.0.0.0-10.255.255.255   
               B类  172.16.0.0-172.31.255.255   
               C类  192.168.0.0-192.168.255.255   
        当然,还有127这个网段是环回地址   
        **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");
        
        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");
        
        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }
    
    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    public static String getRealIP(HttpServletRequest request){
        // 获取客户端ip地址
        String clientIp = request.getHeader("x-forwarded-for");

        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getRemoteAddr();
        }

        String[] clientIps = clientIp.split(",");
        if(clientIps.length <= 1) return clientIp.trim();

        // 判断是否来自CDN
        if(isComefromCDN(request)){
            if(clientIps.length>=2) return clientIps[clientIps.length-2].trim();
        }

        return clientIps[clientIps.length-1].trim();
    }

    private static boolean isComefromCDN(HttpServletRequest request) {
        String host = request.getHeader("host");
        return host.contains("www.189.cn") ||host.contains("shouji.189.cn") || host.contains(
                "image2.chinatelecom-ec.com") || host.contains(
                "image1.chinatelecom-ec.com");
    }
}

来源: http://www.ibloger.net/article/144.html


<think>好的,我现在需要解决用户的问题:配置Apache HTTP服务器,使得当通过域名访问时进行重定向,而通过IP地址访问时不执行重定向。用户希望禁止IP访问时的重定向,仅在域名访问时才触发重定向。 首先,我需要回忆Apache的配置方法,特别是mod_rewrite模块的使用,因为重定向通常需要这个模块。用户提到的引用中有关于mod_rewrite的配置,比如引用[4]提到了RewriteRule和PT标记,这可能相关。 用户的需求是区分域名和IP访问。在Apache中,可以通过检查HTTP请求的Host头来实现这一点。当用户通过域名访问时,Host头是域名;而通过IP访问时,Host头是IP地址。因此,可以在配置中使用条件判断Host头是否为IP,如果是则不做重定向,否则执行重定向。 接下来,我需要确定如何编写RewriteCond来匹配IP地址IP地址的格式可能是IPv4或IPv6,但考虑到大多数情况,可能先处理IPv4。IPv4的正则表达式可以匹配0.0.0.0到255.255.255.255。不过更简单的方式是使用服务器变量SERVER_NAME和REQUEST_URI。或者,可能更直接的是检查HTTP_HOST是否匹配IP地址模式。 例如,判断HTTP_HOST是否是一个IPv4地址,可以使用正则表达式^\\d+\\.\\d+\\.\\d+\\.\\d+$,但这可能不够精确,因为像300.400.500.600这样的无效IP也会被匹配。不过对于配置来说,可能足够,因为用户可能只是不想让直接使用IP访问时触发重定向,而不管这个IP是否有效。 另一个方法是使用ServerName或ServerAlias,但可能不太直接。因此,使用mod_rewrite的RewriteCond更合适。 配置步骤大概如下: 1. 确保mod_rewrite已启用。用户引用[1]和[2]提到需要去掉LoadModule rewrite_module的注释,并重启Apache。所以需要确认用户已经启用了该模块。 2. 在Apache的配置文件(如httpd.conf或虚拟主机配置)中添加重写规则。可能需要放在<Directory>或<VirtualHost>块中。 3. 配置规则的条件:当Host头不是IP地址时,执行重定向。或者相反,当Host头是域名时才重定向。不过用户的需求是当通过域名访问时才重定向,而IP不重定向。因此,可能需要判断Host不是IP地址时进行重定向,但这样可能不太对,因为用户可能希望当访问特定域名时重定向到其他URL,而直接通过IP访问则不触发任何重定向,保持原样。 或者,用户的需求可能是:当用户使用域名访问时,重定向到另一个URL;而使用IP访问时,不进行重定向,直接显示内容。例如,用户希望只有通过www.example.com访问时才被重定向到https或其他路径,而直接通过IP访问则不重定向,可能用于维护页面或其他用途。 假设用户的具体需求是:当访问的是域名时,重定向到另一个地址;当通过IP访问时,不进行重定向。因此,配置应基于Host头判断是否是IP,如果不是(即域名),则重定向。 例如,配置如下: RewriteEngine On RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$ RewriteRule ^(.*)$ http://目标域名/$1 [R=301,L] 但这样可能有问题,因为HTTP_HOST可能包含端口号,比如IP:80,此时正则表达式可能不匹配。因此,更准确的判断可能需要排除IPv4和IPv6的格式。 不过处理IPv6比较复杂,可能暂时先处理IPv4。或者,可以使用Apache的内置变量来判断是否通过IP访问,比如检查SERVER_NAME是否与IP匹配,但可能更复杂。 另一种方法是,用户可能有特定的域名需要重定向,因此可以明确指定域名。例如,如果用户希望当访问的是example.com时重定向到www.example.com,而IP访问不变,则可以: RewriteCond %{HTTP_HOST} ^example\.com$ [NC] RewriteRule ^(.*)$ http://www.example.com/$1 [R=301,L] 但用户的需求更广泛,不是特定域名,而是任何域名访问都重定向,而IP不重定向。或者,用户可能想将多个域名重定向到某个统一地址,而IP不处理。 或者,用户可能希望当访问的是域名时,重定向到HTTPS,而IP访问保持HTTP。例如: RewriteCond %{HTTP_HOST} !^[\d.]+$ RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] 这里,条件检查HTTP_HOST是否不是由数字和点组成(即不是IPv4地址),则重定向到HTTPS。这样,当使用域名访问时,触发重定向,而IP访问则不触发。 但需要确保正则表达式能正确匹配IPv4和可能的IPv6地址IPv6的地址包含冒号,所以可以调整正则表达式。例如,匹配IPv4或IPv6: RewriteCond %{HTTP_HOST} !^([\d.]+|[\da-fA-F:]+)$ 但IPv6可能有更复杂的格式,如压缩的表示法(::1),所以这个正则表达式可能不够准确。不过对于大多数情况,可能足够。 或者,可以使用Apache的变量来判断,例如,使用%{SERVER_ADDR}来获服务器的IP地址,然后与HTTP_HOST比较,但这可能不适用,因为用户可能通过不同的IP访问,或者服务器有多个IP。 因此,更可靠的方法是检查HTTP_HOST是否为IP地址格式。不过精确的正则表达式可能较复杂。例如,对于IPv4: ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ 这可能更准确,但写起来比较麻烦。在配置中,可能使用简化版,如^\d+\.\d+\.\d+\.\d+$,尽管它可能匹配无效的IP,但实际中用户不会用无效IP访问。 所以,配置可能如下: RewriteEngine On RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$ RewriteRule ^(.*)$ http://目标地址/$1 [R=301,L] 这样,当Host头不是IPv4地址时,执行重定向。如果用户希望仅当通过域名访问时才重定向,而IP访问不重定向,这可能有效。 但用户的具体需求可能不同,比如用户可能想将域名访问重定向到另一个路径或HTTPS,而IP访问保持不变。例如,将所有域名访问重定向到HTTPS,而IP访问保持HTTP: RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$ RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] 或者,用户可能希望当通过域名访问时,重定向到另一个域名,而IP访问不重定向。例如: RewriteCond %{HTTP_HOST} ^(www\.)?example\.com$ [NC] RewriteRule ^(.*)$ http://www.newexample.com/$1 [R=301,L] 但用户的问题描述比较笼统,所以需要更通用的解决方案。 另外,用户提到“禁止ip访问时重定向”,即当通过IP访问时,不进行任何重定向,而域名访问时才重定向。这可能意味着用户希望当有人直接用IP访问网站时,显示默认内容或不做处理,而通过域名访问时,则重定向到其他位置。 例如,假设用户希望将所有通过域名example.com的访问重定向到www.example.com,而IP访问不进行任何重定向。则配置可能: RewriteEngine On RewriteCond %{HTTP_HOST} ^example\.com$ [NC] RewriteRule ^(.*)$ http://www.example.com/$1 [R=301,L] 这样,只有当Host头是example.com时才会重定向,而IP访问则不会触发此规则。 但用户的问题更广泛,可能不是针对特定域名,而是任何域名访问都重定向,而IP不重定向。这可能不太常见,但可能的用例是用户希望隐藏真实内容,仅允许通过域名访问,而IP访问显示不同的内容或阻止访问。但根据用户的问题,可能只是需要区分域名和IP的重定向行为。 另外,用户引用[4]提到了RewriteRule中的PT标记,用于与后续的Alias指令配合。可能需要考虑标记的使用,但在这个问题中可能不需要,因为主要是重定向而非内部重写。 总结步骤: 1. 启用mod_rewrite模块,确保配置文件中已消注释LoadModule rewrite_module。 2. 在VirtualHost或全局配置中,添加重写规则,条件判断Host头是否为IP地址,如果不是则执行重定向。 例如: <VirtualHost *:80> ServerName example.com ServerAlias www.example.com RewriteEngine On RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$ RewriteRule ^(.*)$ http://目标域名/$1 [R=301,L] </VirtualHost> 这样,当访问的Host不是IP地址时(即域名),会重定向到目标域名。而直接通过IP访问时,不满足条件,不会触发重定向。 但需要确保目标域名正确,并且用户希望的是这种全局重定向。如果用户希望仅特定域名重定向,则需调整RewriteCond。 可能还需要处理其他情况,比如HTTPS,或者多个域名,但根据问题描述,用户可能只需要基本的配置。 因此,最终的配置可能如下: 在Apache的配置文件中,添加: RewriteEngine On RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ RewriteRule ^(.*)$ http://新域名/$1 [R=301,L] 这样,当Host不是IPv4地址时,重定向到新域名。而通过IP访问则不重定向。 同时,需要确保该配置在正确的上下文中,比如在<Directory>或<VirtualHost>块内,并且mod_rewrite已启用。 此外,用户可能需要处理多个虚拟主机的情况,确保配置在正确的虚拟主机中应用。 另外,用户引用[3]提到了Nginx的配置,但用户的问题是关于Apache的,所以可以忽略。 最后,需要提醒用户修改后重启Apache服务,例如使用`systemctl restart apache2`或`service httpd restart`,具体决于系统。</think>为了实现Apache HTTP服务器在域名访问时重定向,而通过IP地址访问时不重定向,需通过`mod_rewrite`模块的规则配置实现。以下是具体步骤: ### 配置步骤 1. **启用`mod_rewrite`模块** 在Apache配置文件`httpd.conf`中,消以下行的注释以启用模块: ```apache LoadModule rewrite_module modules/mod_rewrite.so ``` 保存后重启Apache服务[^1][^2]。 2. **配置重定向规则** 在虚拟主机配置或全局配置中添加以下规则: ```apache <VirtualHost *:80> # 其他配置项(如DocumentRoot等) RewriteEngine On # 当Host不是IP地址时执行重定向 RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$ RewriteRule ^(.*)$ http://目标域名/$1 [R=301,L] </VirtualHost> ``` - `RewriteCond %{HTTP_HOST} !^\d+\.\d+\.\d+\.\d+$`:检查请求的`Host`头是否为IPv4地址。若否(即域名访问),则触发重定向。 - `RewriteRule`:将请求重定向到目标域名,`R=301`表示永久重定向,`L`表示终止后续规则处理。 3. **重启Apache服务** 保存配置后,执行命令重启Apache: ```bash systemctl restart apache2 # 适用于systemd系统 # 或 service httpd restart # 适用于SysVinit系统 ``` ### 示例场景 - **域名访问**:用户访问`http://example.com`时,会被重定向到`http://目标域名`。 - **IP访问**:用户直接通过`http://192.168.1.1`访问时,不触发重定向,直接显示原内容。 ### 注意事项 - **IPv6支持**:若需排除IPv6地址,需扩展正则表达式,例如`RewriteCond %{HTTP_HOST} !^([\d.]+|[\da-fA-F:]+)$`。 - **端口处理**:若Host头包含端口(如`example.com:8080`),需调整正则表达式为`!^\d+\.\d+\.\d+\.\d+(:\d+)?$`。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值