在ASP.NET MVC中如何获取客户端的IP地址?

337

我完全是ASP.NET MVC堆栈的新手,我想知道简单的Page对象和Request ServerVariables对象发生了什么?

基本上,我想要获取客户端PC的IP地址,但我不明白当前MVC结构如何改变所有这些。

据我所知,大多数变量对象都被HttpRequest变量替代了

有人能分享一些资源吗?在ASP.NET MVC世界中真的有很多东西要学习。 :)

例如,我有一个带有此当前函数的静态类。如何使用ASP.NET MVC获得相同的结果?

public static int getCountry(Page page)
{
    return getCountryFromIP(getIPAddress(page));
}

public static string getIPAddress(Page page)
{
    string szRemoteAddr = page.Request.ServerVariables["REMOTE_ADDR"];
    string szXForwardedFor = page.Request.ServerVariables["X_FORWARDED_FOR"];
    string szIP = "";

    if (szXForwardedFor == null)
    {
        szIP = szRemoteAddr;
    }
    else
    {
        szIP = szXForwardedFor;

        if (szIP.IndexOf(",") > 0)
        {
            string [] arIPs = szIP.Split(',');

            foreach (string item in arIPs)
            {
                if (!isPrivateIP(item))
                {
                    return item;
                }
            }
        }
    }
    return szIP;
}

那么我如何从控制器页面调用这个函数?


https://www.nuget.org/packages/XFF - efaruk
https://dev59.com/q2Ei5IYBdhLWcg3wlc9u - Varun
6个回答

463
简单的答案是使用HttpRequest.UserHostAddress属性示例:在控制器内部:
using System;
using System.Web.Mvc;

namespace Mvc.Controllers
{
    public class HomeController : ClientController
    {
        public ActionResult Index()
        {
            string ip = Request.UserHostAddress;

            ...
        }
    }
}

例子:从帮助类内部:

using System.Web;

namespace Mvc.Helpers
{
    public static class HelperClass
    {
        public static string GetIPHelper()
        {
            string ip = HttpContext.Current.Request.UserHostAddress;
            ..
        }
    }
}
但是,如果请求已经通过一个或多个代理服务器传递,则HttpRequest.UserHostAddress属性返回的IP地址将是中继请求的最后一个代理服务器的IP地址。

代理服务器可能会使用在X-Forwarded-For HTTP头中放置客户端IP地址的事实标准。除了没有保证请求是否具有X-Forwarded-For头之外,还没有保证X-Forwarded-For没有被欺骗


原始答案

Request.UserHostAddress

上面的代码提供了客户端IP地址,而不需要查找一个集合。Request属性在控制器(或视图)内可用。因此,您可以传递一个请求对象来获取相同的结果,而不是传递一个页面类到您的函数中:
public static string getIPAddress(HttpRequestBase request)
{
    string szRemoteAddr = request.UserHostAddress;
    string szXForwardedFor = request.ServerVariables["X_FORWARDED_FOR"];
    string szIP = "";

    if (szXForwardedFor == null)
    {
        szIP = szRemoteAddr;
    }
    else
    {
        szIP = szXForwardedFor;
        if (szIP.IndexOf(",") > 0)
        {
            string [] arIPs = szIP.Split(',');

            foreach (string item in arIPs)
            {
                if (!isPrivateIP(item))
                {
                    return item;
                }
            }
        }
    }
    return szIP;
}

6
由于多个代理服务器可能会转发客户端的HTTP请求,因此可能会存在多个值。如果这些代理服务器是“良好的”(相对于有意匿名或编程不良的情况),则每个代理服务器将在XFF标头中添加前一个代理服务器的IP地址。 - Eric J.
16
isPrivateIP方法是用来判断一个IP地址是否为私有IP地址的。 - Eddie Groves
23
"::1" 表示本地主机。这只是一个简单的说明。 - tomg
7
X-Forwarded-For 头部是由分析数据包并充当中间人的防火墙和负载均衡器添加的。为了保留原始用户的 IP 地址,此头部被添加,以便可以检索原始信息。当数据包被重写时,新的 IP 地址通常是内部 IP,不太有用。 - Marko
1
我看到了"::1",根据@tomg的说法,它是本地主机。我的问题是:当我将我的网站发布到互联网上时,我能否获取IP地址?谢谢。 - Alp
显示剩余7条评论

171

Request.ServerVariables["REMOTE_ADDR"] 应该可以工作 - 直接在视图中或控制器操作方法体中使用(Request 是 MVC 中 Controller 类的属性,而不是 Page)。

它能够工作... 但你必须发布在真正的 IIS 上,而不是虚拟的。


12
我可以使用HttpContext.Current.Request。 - ovolko
23
Adrian的回答(下面)更好 - 它不需要通过魔术字符串进行查找。使用Request.UserHostAddress。 - csauve
1
这总是返回运行我的应用程序的服务器的IP地址。有什么原因吗? - Jack Marchetti
但请注意,如果您的服务器位于负载均衡器或代理后面,则IP可能是该机器的IP。有时可能会设置X-Forwarded-For。 - Boas Enkler
如果您在负载均衡器后面,它将无法工作;public static string UserRealHostAddress(this HttpRequest request, string headerName = "X-FORWARDED-FOR", string seperator = ",", int clientIpIndex = 0) { if (request == null) throw new ArgumentNullException("request"); var rc = request.UserHostAddress; if (request.Headers.AllKeys.Contains(headerName)) { var header = request.Headers[headerName]; var ha = header.Split(new[] {seperator}, StringSplitOptions.RemoveEmptyEntries); rc = ha[clientIpIndex]; } return rc; } - efaruk
显示剩余3条评论

102

这里的代码很有帮助,但为了我的需要,我对其进行了清理并添加了一些测试。以下是最终代码:

using System;
using System.Linq;
using System.Net;
using System.Web;

public class RequestHelpers
{
    public static string GetClientIpAddress(HttpRequestBase request)
    {
        try
        {
            var userHostAddress = request.UserHostAddress;

            // Attempt to parse.  If it fails, we catch below and return "0.0.0.0"
            // Could use TryParse instead, but I wanted to catch all exceptions
            IPAddress.Parse(userHostAddress);

            var xForwardedFor = request.ServerVariables["X_FORWARDED_FOR"];

            if (string.IsNullOrEmpty(xForwardedFor))
                return userHostAddress;

            // Get a list of public ip addresses in the X_FORWARDED_FOR variable
            var publicForwardingIps = xForwardedFor.Split(',').Where(ip => !IsPrivateIpAddress(ip)).ToList();

            // If we found any, return the last one, otherwise return the user host address
            return publicForwardingIps.Any() ? publicForwardingIps.Last() : userHostAddress;
        }
        catch (Exception)
        {
            // Always return all zeroes for any failure (my calling code expects it)
            return "0.0.0.0";
        }
    }

    private static bool IsPrivateIpAddress(string ipAddress)
    {
        // http://en.wikipedia.org/wiki/Private_network
        // Private IP Addresses are: 
        //  24-bit block: 10.0.0.0 through 10.255.255.255
        //  20-bit block: 172.16.0.0 through 172.31.255.255
        //  16-bit block: 192.168.0.0 through 192.168.255.255
        //  Link-local addresses: 169.254.0.0 through 169.254.255.255 (http://en.wikipedia.org/wiki/Link-local_address)

        var ip = IPAddress.Parse(ipAddress);
        var octets = ip.GetAddressBytes();

        var is24BitBlock = octets[0] == 10;
        if (is24BitBlock) return true; // Return to prevent further processing

        var is20BitBlock = octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31;
        if (is20BitBlock) return true; // Return to prevent further processing

        var is16BitBlock = octets[0] == 192 && octets[1] == 168;
        if (is16BitBlock) return true; // Return to prevent further processing

        var isLinkLocalAddress = octets[0] == 169 && octets[1] == 254;
        return isLinkLocalAddress;
    }
}

这里是一些针对该代码的 NUnit 测试(我使用 Rhino Mocks 模拟 HttpRequestBase,在下面的 M<HttpRequestBase> 调用中):

using System.Web;
using NUnit.Framework;
using Rhino.Mocks;
using Should;

[TestFixture]
public class HelpersTests : TestBase
{
    HttpRequestBase _httpRequest;

    private const string XForwardedFor = "X_FORWARDED_FOR";
    private const string MalformedIpAddress = "MALFORMED";
    private const string DefaultIpAddress = "0.0.0.0";
    private const string GoogleIpAddress = "74.125.224.224";
    private const string MicrosoftIpAddress = "65.55.58.201";
    private const string Private24Bit = "10.0.0.0";
    private const string Private20Bit = "172.16.0.0";
    private const string Private16Bit = "192.168.0.0";
    private const string PrivateLinkLocal = "169.254.0.0";

    [SetUp]
    public void Setup()
    {
        _httpRequest = M<HttpRequestBase>();
    }

    [TearDown]
    public void Teardown()
    {
        _httpRequest = null;
    }

    [Test]
    public void PublicIpAndNullXForwardedFor_Returns_CorrectIp()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(null);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void PublicIpAndEmptyXForwardedFor_Returns_CorrectIp()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(string.Empty);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MalformedUserHostAddress_Returns_DefaultIpAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(MalformedIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(null);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(DefaultIpAddress);
    }

    [Test]
    public void MalformedXForwardedFor_Returns_DefaultIpAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(MalformedIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(DefaultIpAddress);
    }

    [Test]
    public void SingleValidPublicXForwardedFor_Returns_XForwardedFor()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(MicrosoftIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }

    [Test]
    public void MultipleValidPublicXForwardedFor_Returns_LastXForwardedFor()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(GoogleIpAddress + "," + MicrosoftIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }

    [Test]
    public void SinglePrivateXForwardedFor_Returns_UserHostAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(Private24Bit);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MultiplePrivateXForwardedFor_Returns_UserHostAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        const string privateIpList = Private24Bit + "," + Private20Bit + "," + Private16Bit + "," + PrivateLinkLocal;
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(privateIpList);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MultiplePublicXForwardedForWithPrivateLast_Returns_LastPublic()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        const string privateIpList = Private24Bit + "," + Private20Bit + "," + MicrosoftIpAddress + "," + PrivateLinkLocal;
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(privateIpList);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }
}

3
这将始终返回运行我的应用程序的服务器的IP地址。 - Jack Marchetti
2
它不应该返回 publicForwardingIps.First() 吗? - andy250
1
@Noah 我猜这对IPv6地址不起作用? - AidanO
不错的解决方案!其他IP地址也应该使用IPAddress.Parse()吗? - Co-der

26

我在使用上面的代码时遇到了问题,需要从控制器中获取IP地址。最终我使用了以下代码:

System.Web.HttpContext.Current.Request.UserHostAddress

3
从控制器中,你只需要使用 HttpContext.Request.UserHostAddress 即可。 - Serj Sagan
谢谢。这正是我需要的帮助类,而不是在控制器或视图上下文中。这是一个很好的通用答案。+1 - Piotr Kula
@gander 你是什么意思?我应该如何编写这个语句? - Piotr Kula
1
在帮助类的顶部,只需编写“using System.Web;”,然后您只需要编写“HttpContext.Current.Request.UserHostAddress”。这仅适用于懒惰的程序员,例如我自己(并解释了Tom的答案和Serj的评论)。 - ganders

19
在一个类中,你可以这样调用它:
public static string GetIPAddress(HttpRequestBase request) 
{
    string ip;
    try
    {
        ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
        if (!string.IsNullOrEmpty(ip))
        {
            if (ip.IndexOf(",") > 0)
            {
                string[] ipRange = ip.Split(',');
                int le = ipRange.Length - 1;
                ip = ipRange[le];
            }
        } else
        {
            ip = request.UserHostAddress;
        }
    } catch { ip = null; }

    return ip; 
}

我在一个razor应用程序中使用它,效果很棒。


为什么要从HTTP_X_FORWARDED_FOR返回最后一个地址?这不是客户端地址而不是第一个地址吗? - Igor Yalovoy
客户端是第一个,这段代码是错误的,使用此代码将获取最后一个代理IP。 - Medo

2

我如何解释我的网站为什么被放在Amazon AWS弹性负载均衡器(ELB)后面:

public class GetPublicIp {

    /// <summary>
    /// account for possbility of ELB sheilding the public IP address
    /// </summary>
    /// <returns></returns>
    public static string Execute() {
        try {
            Console.WriteLine(string.Join("|", new List<object> {
                    HttpContext.Current.Request.UserHostAddress,
                    HttpContext.Current.Request.Headers["X-Forwarded-For"],
                    HttpContext.Current.Request.Headers["REMOTE_ADDR"]
                })
            );

            var ip = HttpContext.Current.Request.UserHostAddress;
            if (HttpContext.Current.Request.Headers["X-Forwarded-For"] != null) {
                ip = HttpContext.Current.Request.Headers["X-Forwarded-For"];
                Console.WriteLine(ip + "|X-Forwarded-For");
            }
            else if (HttpContext.Current.Request.Headers["REMOTE_ADDR"] != null) {
                ip = HttpContext.Current.Request.Headers["REMOTE_ADDR"];
                Console.WriteLine(ip + "|REMOTE_ADDR");
            }
            return ip;
        }
        catch (Exception ex) {
            Console.Error.WriteLine(ex.Message);
        }
        return null;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接