Làm cách nào tôi có thể lấy địa chỉ IP của máy khách trong ASP.NET MVC?


311

Tôi hoàn toàn mới với ngăn xếp ASP.NET MVC và tôi đã tự hỏi điều gì đã xảy ra với đối tượng Trang đơn giản và đối tượng Request ServerVariables?

Về cơ bản, tôi muốn rút địa chỉ IP của PC khách, nhưng tôi không hiểu cấu trúc MVC hiện tại đã thay đổi tất cả như thế nào.

Theo như tôi có thể hiểu, hầu hết các đối tượng biến đã được thay thế bằng các biến thể của httpRequest .

Bất cứ ai quan tâm để chia sẻ một số tài nguyên? Thực sự có một biển thứ để tìm hiểu trong thế giới ASP.NET MVC. :)

Ví dụ, tôi có một lớp tĩnh với hàm hiện tại này. Làm thế nào để tôi có được kết quả tương tự khi sử dụng 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;
}

Và làm thế nào để tôi gọi chức năng này từ trang điều khiển?



Câu trả lời:


427

Câu trả lời đơn giản là sử dụng thuộc tính HttpRequest.UserhostAddress .

Ví dụ: Từ trong Bộ điều khiển:

using System;
using System.Web.Mvc;

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

            ...
        }
    }
}

Ví dụ: Từ trong một lớp người trợ giúp:

using System.Web;

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

NHƯNG, nếu yêu cầu đã được chuyển qua bởi một hoặc nhiều máy chủ proxy thì địa chỉ IP được trả về bởi thuộc tính httpRequest.UserhostAddress sẽ là địa chỉ IP của máy chủ proxy cuối cùng chuyển tiếp yêu cầu.

Các máy chủ proxy CÓ THỂ sử dụng tiêu chuẩn thực tế là đặt địa chỉ IP của máy khách trong tiêu đề X-Forwarded-For HTTP. Ngoài ra, không có gì đảm bảo rằng một yêu cầu có tiêu đề X-Forwarded-For, cũng không có gì đảm bảo rằng X-Forwarded-For chưa được SPOOFED .


Câu trả lời gốc

Request.UserHostAddress

Đoạn mã trên cung cấp địa chỉ IP của Khách hàng mà không cần phải tìm kiếm bộ sưu tập. Thuộc tính Yêu cầu khả dụng trong Bộ điều khiển (hoặc Chế độ xem). Do đó, thay vì chuyển một lớp Trang cho hàm của bạn, bạn có thể truyền một đối tượng Yêu cầu để có kết quả tương tự:

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: Có thể có nhiều giá trị vì nhiều máy chủ proxy có thể được chuyển tiếp theo yêu cầu HTTP của máy khách. Nếu các máy chủ proxy "hoạt động tốt" (trái ngược với các proxy ẩn danh cố ý hoặc chỉ là các proxy được lập trình xấu), thì mỗi máy chủ sẽ xử lý IP của cái trước đó trong tiêu đề XFF.
Eric J.

14
Phương thức isPrivateIP làm gì?
eddiegroves

19
":: 1" có nghĩa là localhost. Chỉ cần một lưu ý đơn giản.
tomg

5
Tiêu đề X-Forwarded-For được thêm bởi tường lửa và bộ cân bằng tải để phân tích các gói và hoạt động như một người đàn ông ở giữa. Để giữ địa chỉ IP gốc của người dùng, tiêu đề này được thêm vào để có thể lấy thông tin gốc. Khi gói được ghi lại, địa chỉ IP mới thường là một ip nội bộ và không hữu ích lắm.
Marko

2
Cảm ơn bạn, điều này đã giúp tôi.
Jack Fairfield

168

Request.ServerVariables["REMOTE_ADDR"] nên hoạt động - trực tiếp trong một khung nhìn hoặc trong thân phương thức hành động của bộ điều khiển (Yêu cầu là một thuộc tính của lớp Trình điều khiển trong MVC, không phải Trang).

Nó đang hoạt động .. nhưng bạn phải xuất bản trên IIS thật chứ không phải ảo.


Làm thế nào để tôi gọi điều này từ phía bộ điều khiển?
hỗn loạn

lol, hey, nó hoạt động, chuyện gì xảy ra nếu tôi muốn đặt nó vào một đối tượng lớp như trên? và tôi vẫn cần đối tượng trang?
hỗn loạn

11
Tôi nghĩ rằng bạn có thể sử dụng HttpContext.C
hiện.Request

23
Câu trả lời của Adrian (bên dưới) tốt hơn nhiều - không cần phải tra cứu bằng chuỗi ma thuật. Sử dụng Request.UserhostAddress
csauve

Điều này luôn trả về địa chỉ IP của máy chủ đang chạy ứng dụng của tôi. Bất kỳ lý do tại sao?
Jack Marchetti

101

Rất nhiều mã ở đây rất hữu ích, nhưng tôi đã làm sạch nó cho mục đích của mình và thêm một số thử nghiệm. Đây là những gì tôi đã kết thúc với:

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

Và đây là một số thử nghiệm NUnit đối với mã đó (Tôi đang sử dụng Rhino Mocks để giả lập httpRequestBase, đây là cuộc gọi M <HttpRequestBase> bên dưới):

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
Điều này luôn trả về địa chỉ IP của máy chủ đang chạy ứng dụng của tôi.
Jack Marchetti

1
Có nên trả lại publicForwardingIps.First()không?
andy250

1
@ Không, tôi đoán điều này sẽ không hoạt động đối với các địa chỉ IPv6?
AidanO

Giải pháp tốt đẹp! IPAddress.Pude () cũng nên được sử dụng trên các địa chỉ IP khác?
Co-der

21

Tôi gặp sự cố khi sử dụng ở trên và tôi cần địa chỉ IP từ bộ điều khiển. Tôi đã sử dụng sau đây cuối cùng:

System.Web.HttpContext.Current.Request.UserHostAddress

2
Từ bộ điều khiển, tất cả những gì bạn phải làm làHttpContext.Request.UserHostAddress
Serj Sagan

Cảm ơn. Đó là những gì tôi cần trong một lớp trợ giúp không phải trong bộ điều khiển hoặc trong bối cảnh xem. Đây là một câu trả lời phổ quát tốt đẹp. +1
Piotr Kula

@gander Ý bạn là gì? Tôi có nên viết tuyên bố?
Piotr Kula

1
Ở đầu lớp của trình trợ giúp, chỉ cần viết "bằng cách sử dụng System.Web;", sau đó bạn chỉ cần viết "HttpContext.Cản.Request.UserhostAddress". Chỉ dành cho những lập trình viên lười biếng, như tôi (và giải thích lý do tại sao câu trả lời của Tom và nhận xét của Serj)
ganders

19

Trong một lớp học, bạn có thể gọi nó như thế này:

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

Tôi đã sử dụng điều này trong một ứng dụng dao cạo với kết quả tuyệt vời.


Tại sao bạn trả lại địa chỉ cuối cùng từ HTTP_X_FORWARDED_FOR? Đây có phải là địa chỉ khách hàng không phải là địa chỉ đầu tiên?
Igor Yalovoy

1

Cách tôi giải thích cho trang web của mình đứng sau Bộ cân bằng tải đàn hồi 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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.