提问



我们有Request.UserHostAddress来获取ASP.NET中的IP地址,但这通常是用户的ISP的IP地址,而不是用户的机器IP地址,例如点击链接。我怎么能得到真正的IP地址?


例如,在Stack Overflow用户配置文件中,它是:上次帐户Activity:4小时前从86.123.127.8,但我的机器IP地址有点不同。 Stack Overflow如何获得此地址?


在某些Web系统中,出于某些目的进行IP地址检查。例如,使用某个IP地址,用户每24小时只能点击下载链接5次?此IP地址应该是唯一的,不适用于拥有大量客户端或Internet用户的ISP。


我理解得好吗?

最佳参考


正如其他人所说,你不能做你要问的问题。如果你描述了你试图解决的问题,也许有人可以提供帮助?例如,你是否试图唯一地识别你的用户?你可以使用cookie,或者会话ID或许而不是IP地址?


编辑您在服务器上看到的地址不应该是ISP的地址,正如您所说的那样,这将是一个巨大的范围。宽带上的家庭用户的地址将是其路由器上的地址,因此房屋内的每个设备都将在外部显示相同,但​​路由器使用NAT来确保流量正确地路由到每个设备。对于从办公室环境访问的用户,所有用户的地址可能相同。使用IP地址进行身份验证的网站存在使其出错的风险 - 您提供的示例很好,而且经常会失败。例如我的办公室在英国,突破点(我出现在互联网上)位于我们主要IT设施所在的另一个国家,所以从我的办公室来看,我的IP地址似乎不在英国。出于这个原因,我无法访问仅限英国的网络内容,例如BBC iPlayer。在任何时候,我公司的数百甚至数千人似乎都是从同一个IP地址访问网络。


在编写服务器代码时,您永远无法确定您看到的IP地址是指的是什么。有些用户喜欢这种方式。有些人故意使用代理或VPN来进一步混淆你。


当您说您的机器地址与StackOverflow上显示的IP地址不同时,您如何找到您的机器地址?如果你只是在本地寻找使用ipconfig或类似的东西,我会期望它因上面概述的原因而有所不同。如果你想仔细检查一下外面世界的看法,请看看whatismyipaddress.com/。[22]


关于NAT的这个维基百科链接将为您提供一些背景信息。[23]

其它参考1


通常,您会想知道访问您网站的人的IP地址。虽然ASP.NET有几种方法可以做到这一点,但我们看到的最佳方法之一是使用ServerVariables集合的HTTP_X_FORWARDED_FOR。


这就是为什么......


有时您的访问者位于代理服务器或路由器后面,标准Request.UserHostAddress仅捕获代理服务器或路由器的IP地址。在这种情况下,用户的IP地址随后存储在服务器变量(HTTP_X_FORWARDED_FOR)中。


所以我们要做的是首先检查HTTP_X_FORWARDED_FOR,如果是空的,我们只需返回ServerVariables("REMOTE_ADDR")


虽然这种方法并非万无一失,但它可以带来更好的结果。下面是VB.NET中的ASP.NET代码,取自James Crowley的博客文章Gotcha:HTTP_X_FORWARDED_FOR返回多个IP地址[24]


C#


protected string GetIPAddress()
{
    System.Web.HttpContext context = System.Web.HttpContext.Current; 
    string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

    if (!string.IsNullOrEmpty(ipAddress))
    {
        string[] addresses = ipAddress.Split(',');
        if (addresses.Length != 0)
        {
            return addresses[0];
        }
    }

    return context.Request.ServerVariables["REMOTE_ADDR"];
}


VB.NET


Public Shared Function GetIPAddress() As String
    Dim context As System.Web.HttpContext = System.Web.HttpContext.Current
    Dim sIPAddress As String = context.Request.ServerVariables("HTTP_X_FORWARDED_FOR")
    If String.IsNullOrEmpty(sIPAddress) Then
        Return context.Request.ServerVariables("REMOTE_ADDR")
    Else
        Dim ipArray As String() = sIPAddress.Split(New [Char]() {","c})
        Return ipArray(0)
    End If
End Function

其它参考2


更新:
感谢Bruno Lopes。如果可能有多个ip地址,则需要使用此方法:


    private string GetUserIP()
    {
        string ipList = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

        if (!string.IsNullOrEmpty(ipList))
        {
            return ipList.Split(',')[0];
        }

        return Request.ServerVariables["REMOTE_ADDR"];
    }

其它参考3


你还认为用户的IP地址是什么?如果你想要网络适配器的IP地址,我恐怕没有办法在Web应用程序中做到这一点。如果您的用户支持NAT或其他东西,您也无法获得IP。


更新:虽然有些网站使用IP来限制用户(例如rapidshare),但它们在NAT环境中无法正常工作。

其它参考4


我想我应该与大家分享我的经验。我在某些情况下看到 REMOTE_ADDR 不会让你得到你想要的东西。例如,如果您在场景后面有一个负载均衡器,如果您正在尝试获取客户端的IP,那么您将遇到麻烦。我使用我的IP掩蔽软件进行了检查,此外我还检查了同事在不同的大陆那么这是我的解决方案。


当我想知道客户端的IP时,我会尝试选择所有可能的证据,以便确定它们是否是唯一的:


在这里,我发现了另一个sever-var,如果你想获得客户端的确切IP,它可以帮助你们。所以我正在使用: HTTP_X_CLUSTER_CLIENT_IP


HTTP_X_CLUSTER_CLIENT_IP 始终可以获取客户端的确切IP。在任何情况下,如果它没有给你价值,你应该寻找 HTTP_X_FORWARDED_FOR ,因为它是第二个获得客户端IP的最佳候选者,然后是 REMOTE_ADDR var可能会也可能不会归还你的知识产权,但对我而言,拥有这三者是我发现监控它们的最佳方法。


我希望这可以帮助一些人。

其它参考5


如果是c#看到这种方式,很简单


string clientIp = (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? 
                   Request.ServerVariables["REMOTE_ADDR"]).Split(',')[0].Trim();

其它参考6


您可以使用:


System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.GetValue(0).ToString();

其它参考7


IP地址是七层堆栈中网络层的一部分。网络层可以对IP地址执行任何操作。那就是代理服务器,NAT,中继等等。


Application层不应以任何方式依赖于IP地址。特别地,IP地址并不意味着除了网络连接的一端的标识符之外的任何其他标识符。一旦连接关闭,您应该期望(同一用户的)IP地址发生变化。

其它参考8


如果您使用的是CloudFlare,
你可以尝试这个扩展方法:


public static class IPhelper
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) return Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();

        return Request.UserHostAddress;
    }
}


然后


string IPAddress = Request.GetIPAddress();

其它参考9


string IP = HttpContext.Current.Request.Params["HTTP_CLIENT_IP"] ?? HttpContext.Current.Request.UserHostAddress;

其它参考10


您可以做的是存储用户的路由器IP以及转发的IP,并尝试使用IP [[外部公共和内部私有]]使其可靠。但是几天之后,客户端可能会从路由器分配新的内部IP,但它会更可靠。

其它参考11


到目前为止,所有响应都考虑了非标准化的,但非常常见,X-Forwarded-For标题。有一个标准化的Forwarded标题,解析起来有点困难。一些例子如下:[25]


Forwarded: for="_gazonk"
Forwarded: For="[2001:db8:cafe::17]:4711"
Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
Forwarded: for=192.0.2.43, for=198.51.100.17


我写了一个类,在确定客户端的IP地址时考虑了这两个头。


using System;
using System.Web;

namespace Util
{
    public static class IP
    {
        public static string GetIPAddress()
        {
            return GetIPAddress(new HttpRequestWrapper(HttpContext.Current.Request));
        }

        internal static string GetIPAddress(HttpRequestBase request)
        {
            // handle standardized 'Forwarded' header
            string forwarded = request.Headers["Forwarded"];
            if (!String.IsNullOrEmpty(forwarded))
            {
                foreach (string segment in forwarded.Split(',')[0].Split(';'))
                {
                    string[] pair = segment.Trim().Split('=');
                    if (pair.Length == 2 && pair[0].Equals("for", StringComparison.OrdinalIgnoreCase))
                    {
                        string ip = pair[1].Trim('"');

                        // IPv6 addresses are always enclosed in square brackets
                        int left = ip.IndexOf('['), right = ip.IndexOf(']');
                        if (left == 0 && right > 0)
                        {
                            return ip.Substring(1, right - 1);
                        }

                        // strip port of IPv4 addresses
                        int colon = ip.IndexOf(':');
                        if (colon != -1)
                        {
                            return ip.Substring(0, colon);
                        }

                        // this will return IPv4, "unknown", and obfuscated addresses
                        return ip;
                    }
                }
            }

            // handle non-standardized 'X-Forwarded-For' header
            string xForwardedFor = request.Headers["X-Forwarded-For"];
            if (!String.IsNullOrEmpty(xForwardedFor))
            {
                return xForwardedFor.Split(',')[0];
            }

            return request.UserHostAddress;
        }
    }
}


以下是我用来验证解决方案的一些单元测试:


using System.Collections.Specialized;
using System.Web;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UtilTests
{
    [TestClass]
    public class IPTests
    {
        [TestMethod]
        public void TestForwardedObfuscated()
        {
            var request = new HttpRequestMock("for=\"_gazonk\"");
            Assert.AreEqual("_gazonk", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv6()
        {
            var request = new HttpRequestMock("For=\"[2001:db8:cafe::17]:4711\"");
            Assert.AreEqual("2001:db8:cafe::17", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4()
        {
            var request = new HttpRequestMock("for=192.0.2.60;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4WithPort()
        {
            var request = new HttpRequestMock("for=192.0.2.60:443;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedMultiple()
        {
            var request = new HttpRequestMock("for=192.0.2.43, for=198.51.100.17");
            Assert.AreEqual("192.0.2.43", Util.IP.GetIPAddress(request));
        }
    }

    public class HttpRequestMock : HttpRequestBase
    {
        private NameValueCollection headers = new NameValueCollection();

        public HttpRequestMock(string forwarded)
        {
            headers["Forwarded"] = forwarded;
        }

        public override NameValueCollection Headers
        {
            get { return this.headers; }
        }
    }
}

其它参考12


在ashx文件中使用


public string getIP(HttpContext c)
{
    string ips = c.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    if (!string.IsNullOrEmpty(ips))
    {
        return ips.Split(',')[0];
    }
    return c.Request.ServerVariables["REMOTE_ADDR"];
}

其它参考13


结合@Tony和@mangokun的答案,我创建了以下扩展方法:


public static class RequestExtensions
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null)
        {
            string ipAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length != 0)
                {
                    return addresses[0];
                }
            }
        }

        return Request.UserHostAddress;
    }
}

其它参考14


用这个


Dns.GetHostEntry(Dns.GetHostName())

其它参考15


尝试:


using System.Net;

public static string GetIpAddress()  // Get IP Address
{
    string ip = "";     
    IPHostEntry ipEntry = Dns.GetHostEntry(GetCompCode());
    IPAddress[] addr = ipEntry.AddressList;
    ip = addr[2].ToString();
    return ip;
}
public static string GetCompCode()  // Get Computer Name
{   
    string strHostName = "";
    strHostName = Dns.GetHostName();
    return strHostName;
}