Bagaimana saya bisa mendapatkan alamat IP klien di ASP.NET MVC?

311

Saya benar-benar baru di ASP.NET MVC stack, dan saya bertanya-tanya apa yang terjadi pada objek Halaman sederhana dan objek Request ServerVariables?

Pada dasarnya, saya ingin mengeluarkan alamat IP PC klien, tetapi saya gagal memahami bagaimana struktur MVC saat ini telah mengubah semua ini.

Sejauh yang saya bisa mengerti, sebagian besar objek variabel telah digantikan oleh varian HttpRequest .

Adakah yang mau berbagi beberapa sumber daya? Benar-benar ada banyak hal untuk dipelajari di dunia ASP.NET MVC. :)

Sebagai contoh, saya memiliki kelas statis dengan fungsi ini. Bagaimana cara mendapatkan hasil yang sama menggunakan 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;
}

Dan bagaimana saya memanggil fungsi ini dari halaman pengontrol?

melaos
sumber

Jawaban:

427

Jawaban sederhananya adalah dengan menggunakan properti HttpRequest.UserHostAddress .

Contoh: Dari dalam Kontroler:

using System;
using System.Web.Mvc;

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

            ...
        }
    }
}

Contoh: Dari dalam kelas pembantu:

using System.Web;

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

TETAPI, jika permintaan telah diteruskan oleh satu, atau lebih, server proxy , maka alamat IP yang dikembalikan oleh HttpRequest.UserHostAddress properti akan menjadi alamat IP dari server proxy terakhir yang menyampaikan permintaan.

Server proxy MUNGKIN menggunakan standar de facto menempatkan alamat IP klien di header X-Forwarded-For HTTP. Selain tidak ada jaminan bahwa permintaan memiliki header X-Forwarded-For, juga tidak ada jaminan bahwa X-Forwarded-For belum SPOOFED .


Jawaban Asli

Request.UserHostAddress

Kode di atas memberikan alamat IP Klien tanpa harus mencari koleksi. Properti Permintaan tersedia dalam Pengendali (atau Tampilan). Karena itu alih-alih meneruskan kelas Halaman ke fungsi Anda, Anda dapat melewati objek Permintaan untuk mendapatkan hasil yang sama:

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
sumber
6
@ makerofthings7: Mungkin ada beberapa nilai karena beberapa server proxy mungkin meneruskan sepanjang permintaan HTTP klien. Jika server proxy "berperilaku baik" (sebagai lawan dari proxy anonim yang sengaja atau hanya yang diprogram dengan buruk), masing-masing akan menempel pada IP yang sebelumnya di header XFF.
Eric J.
14
Apa yang dilakukan metode isPrivateIP?
eddiegroves
19
":: 1" berarti localhost. Hanya sebuah catatan sederhana.
tomg
5
Header X-Forwarded-For ditambahkan oleh firewall dan load balancers yang menganalisis paket dan bertindak sebagai man di tengah. Untuk menjaga alamat ip pengguna asli, tajuk ini ditambahkan sehingga informasi asli dapat diambil. Ketika paket ditulis ulang, alamat ip baru biasanya merupakan ip internal dan tidak terlalu berguna.
Marko
2
Terima kasih, ini membantu saya.
Jack Fairfield
168

Request.ServerVariables["REMOTE_ADDR"] harus bekerja - baik secara langsung dalam tampilan atau di badan metode tindakan kontroler (Permintaan adalah properti dari kelas Controller di MVC, bukan Halaman).

Ini berfungsi .. tetapi Anda harus menerbitkan pada IIS nyata bukan yang virtual.

ovolko
sumber
bagaimana saya menyebutnya dari sisi pengontrol?
melaos
lol, hei itu berhasil, apa yang terjadi jika saya ingin memasukkannya ke objek kelas seperti di atas? dan apakah saya masih membutuhkan objek halaman?
melaos
11
Saya pikir Anda bisa menggunakan HttpContext.Current.Request
ovolko
23
Jawaban Adrian (di bawah) jauh lebih baik - tidak perlu melakukan pencarian dengan string sihir. Gunakan
Permintaan.UserHostAddress
Ini selalu mengembalikan alamat IP server yang menjalankan aplikasi saya. Apa alasannya?
Jack Marchetti
101

Banyak kode di sini sangat membantu, tetapi saya membersihkannya untuk keperluan saya dan menambahkan beberapa tes. Inilah yang akhirnya saya dapatkan:

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

Dan berikut adalah beberapa tes NUnit terhadap kode itu (saya menggunakan Rhino Mocks untuk mengejek HttpRequestBase, yang merupakan panggilan M <HttpRequestBase> di bawah):

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);
    }
}
Nuh Heldman
sumber
2
Ini selalu mengembalikan alamat IP server yang menjalankan aplikasi saya.
Jack Marchetti
1
Tidakkah seharusnya mengembalikan publicForwardingIps.First()?
andy250
1
@Noah saya kira ini tidak akan bekerja untuk alamat IPv6?
AidanO
Solusi bagus! Haruskah IPAddress.Parse () juga digunakan pada alamat IP lainnya?
Co-der
21

Saya mengalami masalah dalam menggunakan di atas, dan saya membutuhkan alamat IP dari pengontrol. Saya menggunakan yang berikut ini pada akhirnya:

System.Web.HttpContext.Current.Request.UserHostAddress
Tom
sumber
2
Dari pengontrol yang harus Anda lakukan adalahHttpContext.Request.UserHostAddress
Serj Sagan
Terima kasih. Itu yang saya butuhkan di kelas pembantu tidak di controller atau dalam konteks tampilan. Ini adalah jawaban universal yang bagus. +1
Piotr Kula
@ander Apa maksudmu? Bagaimana saya harus menulis pernyataan?
Piotr Kula
1
Di bagian atas kelas pembantu, cukup tulis "menggunakan System.Web;", maka Anda hanya perlu menulis "HttpContext.Current.Request.UserHostAddress". Hanya untuk pemrogram yang malas, seperti saya (dan menjelaskan mengapa jawaban Tom dan komentar Serj)
ganders
19

Di kelas Anda mungkin menyebutnya seperti ini:

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

Saya menggunakan ini di aplikasi pisau cukur dengan hasil yang bagus.

Paul Keefe
sumber
Mengapa Anda mengembalikan alamat terakhir dari HTTP_X_FORWARDED_FOR? Apakah ini alamat klien bukan yang pertama?
Igor Yalovoy
1

Bagaimana saya menjelaskan situs saya berada di belakang ELS ( Amazon Load Loader Elastis):

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
sumber