Comment puis-je obtenir l'adresse IP du client dans ASP.NET MVC?

311

Je suis totalement nouveau dans la pile ASP.NET MVC et je me demandais ce qui était arrivé à l'objet Page simple et à l'objet Request ServerVariables?

Fondamentalement, je veux retirer l'adresse IP du PC client, mais je n'arrive pas à comprendre comment la structure MVC actuelle a changé tout cela.

Pour autant que je puisse comprendre, la plupart des objets variables ont été remplacés par les variantes HttpRequest .

Quelqu'un veut-il partager des ressources? Il y a vraiment une mer de choses à apprendre dans le monde ASP.NET MVC. :)

Par exemple, j'ai une classe statique avec cette fonction actuelle. Comment obtenir le même résultat en utilisant 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;
}

Et comment appeler cette fonction depuis la page du contrôleur?

melaos
la source

Réponses:

427

La réponse simple consiste à utiliser la propriété HttpRequest.UserHostAddress .

Exemple: à partir d'un contrôleur:

using System;
using System.Web.Mvc;

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

            ...
        }
    }
}

Exemple: à partir d'une classe d'assistance:

using System.Web;

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

MAIS, si la demande a été transmise par un ou plusieurs serveurs proxy , l'adresse IP renvoyée par la propriété HttpRequest.UserHostAddress sera l'adresse IP du dernier serveur proxy qui a relayé la demande.

Les serveurs proxy PEUVENT utiliser la norme de facto de placer l'adresse IP du client dans l'en - tête HTTP X-Forwarded-For . En plus de cela, il n'y a aucune garantie qu'une requête a un en-tête X-Forwarded-For, il n'y a également aucune garantie que le X-Forwarded-For n'a pas été SPOOFED .


Réponse originale

Request.UserHostAddress

Le code ci-dessus fournit l'adresse IP du client sans recourir à la recherche d'une collection. La propriété Request est disponible dans les contrôleurs (ou les vues). Par conséquent, au lieu de passer une classe Page à votre fonction, vous pouvez passer un objet Request pour obtenir le même résultat:

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
la source
6
@ makerofthings7: Il peut y avoir plusieurs valeurs car plusieurs serveurs proxy peuvent transmettre le long de la requête HTTP du client. Si les serveurs proxy sont "bien comportés" (par opposition aux proxys intentionnellement anonymes ou simplement mal programmés), chacun clouera sur l'IP du précédent dans l'en-tête XFF.
Eric J.
14
Que fait la méthode isPrivateIP?
eddiegroves
19
":: 1" signifie localhost. Juste une simple note.
tomg
5
L'en-tête X-Forwarded-For est ajouté par des pare-feu et des équilibreurs de charge qui analysent les paquets et agissent comme un homme au milieu. Afin de conserver l'adresse IP de l'utilisateur d'origine, cet en-tête est ajouté afin que les informations d'origine puissent être récupérées. Lorsque le paquet est réécrit, la nouvelle adresse IP est généralement une adresse IP interne et n'est pas très utile.
Marko
2
Merci, cela m'a aidé.
Jack Fairfield
168

Request.ServerVariables["REMOTE_ADDR"] devrait fonctionner - soit directement dans une vue, soit dans le corps de la méthode d'action du contrôleur (Request est une propriété de la classe Controller dans MVC, pas Page).

Cela fonctionne .. mais vous devez publier sur un vrai IIS pas le virtuel.

ovolko
la source
comment puis-je appeler cela du côté du contrôleur?
melaos
lol, hé ça marche, que se passe-t-il si je souhaite mettre cela dans un objet de classe comme ci-dessus? et ai-je toujours besoin de l'objet page?
melaos
11
Je pense que vous pourriez utiliser HttpContext.Current.Request
ovolko
23
La réponse d'Adrian (ci-dessous) est bien meilleure - il n'est pas nécessaire d'effectuer une recherche par chaîne magique. Utilisez Request.UserHostAddress
csauve
Cela renvoie toujours l'adresse IP du serveur exécutant mon application. Une raison pourquoi?
Jack Marchetti
101

Une grande partie du code ici a été très utile, mais je l'ai nettoyé à mes fins et ajouté quelques tests. Voici ce que j'ai fini avec:

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

Et voici quelques tests NUnit contre ce code (j'utilise Rhino Mocks pour se moquer de HttpRequestBase, qui est l'appel M <HttpRequestBase> ci-dessous):

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
la source
2
Cela renvoie toujours l'adresse IP du serveur exécutant mon application.
Jack Marchetti
1
Ne doit-il pas retourner le publicForwardingIps.First()?
andy250
1
@Noah, je suppose que cela ne fonctionnera pas pour les adresses IPv6?
AidanO
Bonne solution! IPAddress.Parse () doit-il également être utilisé sur les autres adresses IP?
Co-der
21

J'ai eu du mal à utiliser ce qui précède et j'avais besoin de l'adresse IP d'un contrôleur. J'ai finalement utilisé ce qui suit:

System.Web.HttpContext.Current.Request.UserHostAddress
À M
la source
2
Du contrôleur, tout ce que vous aviez à faire étaitHttpContext.Request.UserHostAddress
Serj Sagan
Merci. C'est ce dont j'avais besoin dans une classe d'assistance, pas dans un contrôleur ou dans un contexte de vue. C'est une belle réponse universelle. +1
Piotr Kula
@gander Que voulez-vous dire? Comment devrais-je écrire la déclaration?
Piotr Kula
1
En haut de la classe d'assistance, écrivez simplement "using System.Web;", puis il vous suffit d'écrire "HttpContext.Current.Request.UserHostAddress". Juste pour les programmeurs paresseux, comme moi (et explique pourquoi la réponse de Tom et le commentaire de Serj)
ganders
19

Dans une classe, vous pourriez l'appeler comme ceci:

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

Je l'ai utilisé dans une application de rasoir avec d'excellents résultats.

Paul Keefe
la source
Pourquoi renvoyez-vous la dernière adresse de HTTP_X_FORWARDED_FOR? Est-ce que l'adresse du client n'est pas la première?
Igor Yalovoy
1

Comment je compte que mon site est derrière un Amazon AWS Elastic Load Balancer (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
la source