Jak mogę uzyskać adres IP klienta w ASP.NET MVC?

311

Jestem zupełnie nowy w stosie ASP.NET MVC i zastanawiałem się, co stało się z prostym obiektem Page i obiektem Request ServerVariables?

Zasadniczo chcę wyciągnąć adres IP komputera klienckiego, ale nie rozumiem, jak zmieniła to wszystko obecna struktura MVC.

O ile rozumiem, większość obiektów zmiennych została zastąpiona wariantami HttpRequest .

Czy ktoś chce udostępnić jakieś zasoby? W świecie ASP.NET MVC naprawdę jest mnóstwo rzeczy do nauczenia się. :)

Na przykład mam klasę statyczną z tą bieżącą funkcją. Jak uzyskać ten sam wynik za pomocą 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;
}

I jak wywołać tę funkcję ze strony kontrolera?

Melaos
źródło

Odpowiedzi:

427

Prostą odpowiedzią jest użycie właściwości HttpRequest.UserHostAddress .

Przykład: z poziomu kontrolera:

using System;
using System.Web.Mvc;

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

            ...
        }
    }
}

Przykład: z poziomu klasy pomocnika:

using System.Web;

namespace Mvc.Helpers
{
    public static class HelperClass
    {
        public static string GetIPHelper()
        {
            string ip = HttpContext.Current.Request.UserHostAddress;
            ..
        }
    }
}

ALE, jeśli żądanie zostało przekazane przez jeden lub więcej serwerów proxy, wówczas adres IP zwrócony przez HttpRequest.UserHostAddress będzie adresem IP ostatniego serwera proxy, który przekazał żądanie.

Serwery proxy MOGĄ używać de facto standardu umieszczania adresu IP klienta w nagłówku HTTP X-Forwarded-For . Poza tym nie ma gwarancji, że żądanie ma nagłówek X-Forwarded-For, nie ma również gwarancji, że X-Forwarded-For nie został SPOOFED .


Oryginalna odpowiedź

Request.UserHostAddress

Powyższy kod podaje adres IP klienta bez konieczności wyszukiwania kolekcji. Właściwość Request jest dostępna w ramach kontrolerów (lub widoków). Dlatego zamiast przekazywać klasę Page do funkcji, możesz przekazać obiekt Request, aby uzyskać ten sam wynik:

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;
}
Adrian Toman
źródło
6
@ makerofthings7: Może istnieć wiele wartości, ponieważ wiele serwerów proxy może przekazywać dalej na żądanie HTTP klienta. Jeśli serwery proxy są „dobrze zachowane” (w przeciwieństwie do celowo anonimowych lub po prostu źle zaprogramowanych serwerów proxy), każdy poda adres IP poprzedniego w nagłówku XFF.
Eric J.
14
Do czego służy metoda isPrivateIP?
eddiegroves,
19
„:: 1” oznacza localhost. Tylko prosta notatka.
tomg
5
Nagłówek X-Forwarded-For jest dodawany przez zapory ogniowe i moduły równoważenia obciążenia, które analizują pakiety i działają jak człowiek pośrodku. W celu zachowania adresu IP pierwotnego użytkownika ten nagłówek jest dodawany, aby można było odzyskać oryginalne informacje. Kiedy pakiet jest przepisywany, nowy adres IP jest zwykle wewnętrznym adresem ip i niezbyt przydatny.
Marko
2
Dziękuję, to mi pomogło.
Jack Fairfield
168

Request.ServerVariables["REMOTE_ADDR"] powinien działać - albo bezpośrednio w widoku, albo w treści metody akcji kontrolera (Request jest właściwością klasy Controller w MVC, a nie Page).

Działa .. ale musisz publikować na prawdziwym IIS, a nie wirtualnym.

ovolko
źródło
jak mogę to nazwać od strony kontrolera?
melaos,
lol, hej, to działa, co się stanie, jeśli chcę umieścić to w obiekcie klasy jak wyżej? i czy nadal potrzebuję obiektu strony?
melaos,
11
Myślę, że możesz użyć HttpContext.Current.Request
ovolko
23
Odpowiedź Adriana (poniżej) jest znacznie lepsza - nie trzeba sprawdzać magicznego sznurka. Użyj Request.UserHostAddress
csauve
To zawsze zwraca adres IP serwera, na którym działa moja aplikacja. Jakiś powód dlaczego?
Jack Marchetti
101

Wiele kodu tutaj było bardzo pomocne, ale wyczyściłem go dla moich celów i dodałem kilka testów. Oto, z czym skończyłem:

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;
    }
}

A oto kilka testów NUnit dla tego kodu (używam Rhino Mocks, aby wyśmiewać HttpRequestBase, czyli wywołanie M <HttpRequestBase> poniżej):

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);
    }
}
Noah Heldman
źródło
2
To zawsze zwraca adres IP serwera, na którym działa moja aplikacja.
Jack Marchetti
1
Czy to nie powinno zwrócić publicForwardingIps.First()?
andy250,
1
@ Nie, zgaduję, że to nie zadziała dla adresów IPv6?
AidanO
Fajne rozwiązanie! Czy IPAddress.Parse () należy również używać na innych adresach IP?
Co-der
21

Miałem problem z użyciem powyższego i potrzebowałem adresu IP z kontrolera. W końcu zastosowałem następujące:

System.Web.HttpContext.Current.Request.UserHostAddress
Tomek
źródło
2
Ze sterownika wszystko, co musiałem zrobić, toHttpContext.Request.UserHostAddress
Serj Sagan
Dzięki. Tego potrzebowałem w klasie pomocnika, a nie w kontrolerze lub w kontekście widoku. To ładna uniwersalna odpowiedź. +1
Piotr Kula,
@ gander Co masz na myśli? Ho powinienem napisać oświadczenie?
Piotr Kula
1
Na górze klasy pomocnika po prostu napisz „using System.Web;”, a następnie wystarczy napisać „HttpContext.Current.Request.UserHostAddress”. Tylko dla leniwych programistów, takich jak ja (i wyjaśnia, dlaczego odpowiedź Toma i komentarz
Serja
19

W klasie możesz to tak nazwać:

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; 
}

Użyłem tego w aplikacji maszynki do golenia ze świetnymi wynikami.

Paul Keefe
źródło
Dlaczego zwracasz ostatni adres z HTTP_X_FORWARDED_FOR? Czy to adres klienta nie jest pierwszy?
Igor Yalovoy
1

Jak rozliczam moją witrynę za moduł równoważenia obciążenia elastycznego 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;
    }
}
sobelito
źródło