ฉันจะรับที่อยู่ IP ของลูกค้าใน ASP.NET MVC ได้อย่างไร


311

ฉันใหม่ทั้งหมดในกอง ASP.NET MVC และฉันสงสัยว่าเกิดอะไรขึ้นกับวัตถุหน้าอย่างง่ายและวัตถุ ServerVariables คำขอ?

โดยพื้นฐานแล้วฉันต้องการดึงที่อยู่ 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;
}

และฉันจะเรียกใช้ฟังก์ชันนี้จากหน้าควบคุมได้อย่างไร



คำตอบ:


427

คำตอบง่ายๆคือการใช้คุณสมบัติ 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;
            ..
        }
    }
}

แต่ ถ้าการร้องขอผ่านหนึ่งพร็อกซีเซิร์ฟเวอร์หรือมากกว่านั้นที่อยู่ IP ที่ส่งคืนโดยHttpRequest.UserHostAddressจะเป็นที่อยู่ IP ของพร็อกซีเซิร์ฟเวอร์สุดท้ายที่ถ่ายทอดการร้องขอ

เซิร์ฟเวอร์พร็อกซี่อาจใช้พฤตินัยมาตรฐานของการวางที่อยู่ IP ของลูกค้าในX-Forwarded-Forหัว HTTP นอกเหนือจากการมีการรับประกันว่าคำขอมีไม่มี X-Forwarded-For หัวนอกจากนี้ยังมีการรับประกันว่า X-Forwarded-For ไม่ได้รับไม่หัวหมุน


คำตอบเดิม

Request.UserHostAddress

รหัสด้านบนแสดงที่อยู่ IP ของลูกค้าโดยไม่ต้องหันไปหาชุดสะสม คุณสมบัติคำขอมีอยู่ในตัวควบคุม (หรือมุมมอง) ดังนั้นแทนที่จะส่งคลาสเพจไปยังฟังก์ชันของคุณคุณสามารถส่งวัตถุคำขอเพื่อให้ได้ผลลัพธ์เดียวกัน:

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
@ makerofthings7: อาจมีหลายค่าเนื่องจากหลายพร็อกซีเซิร์ฟเวอร์อาจส่งต่อตามคำขอ HTTP ของลูกค้า หากพร็อกซีเซิร์ฟเวอร์นั้น "ประพฤติตัวดี" (ตรงข้ามกับพร็อกซีที่ไม่ระบุชื่อโดยเจตนาหรือเป็นโปรแกรมที่ไม่ดี) แต่ละอันจะทำการดักฟัง IP ของอันก่อนหน้านี้ในส่วนหัว XFF
Eric J.

14
วิธี isPrivateIP ทำอะไร
eddiegroves

19
":: 1" หมายถึง localhost เพียงทราบง่ายๆ
tomg

5
ส่วนหัว X-Forwarded-For ถูกเพิ่มโดยไฟร์วอลล์และตัวโหลดบาลานซ์ที่วิเคราะห์แพ็กเก็ตและทำหน้าที่เป็นคนตรงกลาง เพื่อรักษาที่อยู่ IP ของผู้ใช้เดิมส่วนหัวนี้จะถูกเพิ่มเพื่อให้สามารถดึงข้อมูลต้นฉบับได้ เมื่อแพ็กเก็ตถูกเขียนใหม่ที่อยู่ IP ใหม่มักจะเป็น IP ภายในและไม่มีประโยชน์มาก
Marko

2
ขอบคุณสิ่งนี้ช่วยฉัน
Jack Fairfield

168

Request.ServerVariables["REMOTE_ADDR"] ควรทำงาน - โดยตรงในมุมมองหรือในร่างกายวิธีการดำเนินการควบคุม (ขอเป็นทรัพย์สินของคลาสควบคุมใน MVC ไม่ใช่หน้า)

มันใช้งานได้ .. แต่คุณต้องทำการเผยแพร่บน IIS จริงไม่ใช่ตัวเสมือน


ฉันจะเรียกสิ่งนี้จากด้านควบคุมได้อย่างไร
melaos

ฮ่า ๆ เฮ้ที่ได้ผลจะเกิดอะไรขึ้นถ้าฉันต้องการใส่มันลงในคลาสของวัตถุดังกล่าวข้างต้น และฉันยังต้องการวัตถุหน้าหรือไม่
melaos

11
ฉันคิดว่าคุณสามารถใช้ HttpContext.Current.Request
ovolko

23
คำตอบของเอเดรียน (ด้านล่าง) นั้นดีกว่ามาก - ไม่จำเป็นต้องค้นหาด้วยสายเวท ใช้ Request.UserHostAddress
csauve

นี่จะส่งคืนที่อยู่ IP ของเซิร์ฟเวอร์ที่เรียกใช้แอปของฉัน มีเหตุผลอะไร
Jack Marchetti

101

รหัสจำนวนมากที่นี่มีประโยชน์มาก แต่ฉันล้างมันเพื่อจุดประสงค์ของฉันและเพิ่มการทดสอบบางอย่าง นี่คือสิ่งที่ฉันลงเอยด้วย:

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 เทียบกับรหัสนั้น (ฉันใช้แรด 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);
    }
}

2
นี่จะส่งคืนที่อยู่ IP ของเซิร์ฟเวอร์ที่ใช้แอปพลิเคชันของฉันเสมอ
แจ็ค Marchetti

1
มันจะไม่ส่งคืนpublicForwardingIps.First()หรือไม่?
andy250

1
@ ไม่มีฉันเดาว่านี่จะไม่ทำงานกับที่อยู่ IPv6 ใช่ไหม
AidanO

ทางออกที่ดี! IPAddress.Parse () ควรใช้กับที่อยู่ IP อื่นหรือไม่
Co-der

21

ฉันมีปัญหาในการใช้งานด้านบนและฉันต้องการที่อยู่ IP จากคอนโทรลเลอร์ ฉันใช้สิ่งต่อไปนี้ในท้ายที่สุด:

System.Web.HttpContext.Current.Request.UserHostAddress

2
จากคอนโทรลเลอร์สิ่งที่คุณต้องทำคือHttpContext.Request.UserHostAddress
Serj Sagan

ขอบคุณ นั่นคือสิ่งที่ฉันต้องการในคลาสผู้ช่วยที่ไม่ได้อยู่ในคอนโทรลเลอร์หรือในบริบทของมุมมอง นี่คือคำตอบสากลที่ดี +1
Piotr Kula

@gander คุณหมายถึงอะไร? ฉันควรเขียนข้อความนี้ใหม
Piotr Kula

1
ที่ด้านบนสุดของคลาสตัวช่วยเพียงแค่เขียน "using System.Web;" แล้วคุณจะต้องเขียน "HttpContext.Current.Request.UserHostAddress" สำหรับโปรแกรมเมอร์ที่ขี้เกียจอย่างฉัน (และอธิบายว่าทำไมคำตอบของทอมและความคิดเห็นของ 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; 
}

ฉันใช้สิ่งนี้ในแอปมีดโกนที่มีผลลัพธ์ที่ยอดเยี่ยม


ทำไมคุณถึงส่งกลับที่อยู่ล่าสุดจาก HTTP_X_FORWARDED_FOR มันเป็นที่อยู่ลูกค้าไม่ใช่คนแรก?
Igor Yalovoy

1

ฉันจะบัญชีเว็บไซต์ของฉันที่อยู่เบื้องหลัง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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.