اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
پنج دقیقه
در بسیاری از سناریوها این موضوع مطرح میشود که سرویسهای طراحی شده بر
اساس Asp.Net Web Api، فقط به یک سری آی پیهای مشخص سرویس دهند. برای مثال
اگر Ip کلاینت در لیست کلاینتهای دارای لایسنس خریداری شده بود، امکان
استفاده از سرویس میسر باشد؛ در غیر این صورت خیر. بسته به نوع پیاده سازی
سرویسهای Web api، پیاده سازی این بخش کمی متفاوت خواهد شد. در طی این پست این
موضوع را برای سه حالت IIs Host و SelfHost و Owin Host بررسی میکنیم.
در اینجا قصد داریم حالتی را پیاده سازی نماییم که اگر درخواست جاری از سوی کلاینتی بود که Ip آن در لیست Ipهای غیر مجاز قرار داشت، ادامهی عملیات متوقف شود.IIS Hosting:
حالت پیش فرض استفاده از سرویسهای Web Api همین گزینه است؛ وابستگی
مستقیم به System.Web . در مورد مزایا و معایب آن بحث نمیکنیم اما اگر این
روش را انتخاب کردید تکه کد زیر این کار را برای ما انجام میدهد:
if (request.Properties.ContainsKey["MS_HttpContext"]) { var ctx = request.Properties["MS_HttpContext"] as HttpContextWrapper; if (ctx != null) { var ip = ctx.Request.UserHostAddress; } }
public static class HttpRequestMessageExtensions { private const string HttpContext = "MS_HttpContext"; public static string GetClientIpAddress(this HttpRequestMessage request) { if (request.Properties.ContainsKey(HttpContext)) { dynamic ctx = request.Properties[HttpContext]; if (ctx != null) { return ctx.Request.UserHostAddress; } } return null; } }
Self Hosting:
در حالت Self Host میتوان عملیات بالا را با استفاده از خاصیت RemoteEndpointMessageProperty انجام داد که تقریبا شبیه به حالت Web Host است. مقدار این خاصیت نیز در شی جاری HttpRequestMessage وجود دارد. فقط باید به صورت زیر آن را واکشی نماییم:
if (request.Properties.ContainsKey[RemoteEndpointMessageProperty.Name]) { var remote = request.Properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; if (remote != null) { var ip = remote.Address; } }
خاصیت RemoteEndpointMessageProperty به تمامی
درخواستها وارده در سرویسهای WCF چه در حالت استفاده از Http و چه در
حالت Tcp اضافه میشود و در اسمبلی System.ServiceModel نیز میباشد. ار آنجا که
Web Api از هستهی WCF استفاده میکند (WCF Core) در نتیجه میتوان از این
روش استفاده نمود. فقط باید اسمبلی System.ServiceModel را به پروژهی خود
اضافه نمایید.
ترکیب حالتهای قبلی:
اگر میخواهید کدهای نوشته شده شما وابستگی به نوع هاست پروژه نداشته باشد،
یا به معنای دیگر، در هر دو حالت به درستی کار کند میتوانید به روش زیر
حالتهای قبلی را با هم ترکیب کنید.
»در این صورت دیگر نیازی به اضافه کردن اسمبلی System.ServiceModel نیست.
»در این صورت دیگر نیازی به اضافه کردن اسمبلی System.ServiceModel نیست.
public static class HttpRequestMessageExtensions { private const string HttpContext = "MS_HttpContext"; private const string RemoteEndpointMessage = "System.ServiceModel.Channels.RemoteEndpointMessageProperty"; public static string GetClientIpAddress(this HttpRequestMessage request) { if (request.Properties.ContainsKey(HttpContext)) { dynamic ctx = request.Properties[HttpContext]; if (ctx != null) { return ctx.Request.UserHostAddress; } } if (request.Properties.ContainsKey(RemoteEndpointMessage)) { dynamic remoteEndpoint = request.Properties[RemoteEndpointMessage]; if (remoteEndpoint != null) { return remoteEndpoint.Address; } } return null; } }
public class MyHandler : DelegatingHandler { private readonly HashSet<string> deniedIps; protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (deniedIps.Contains(request.GetClientIpAddress())) { return Task.FromResult( new HttpResponseMessage( HttpStatusCode.Unauthorized ) ); } return base.SendAsync(request, cancellationToken); } }
Owin :
زمانی که از Owin برای هاست سرویسهای Web Api خود استفاده میکنید کمی روال انجام کار متفاوت خواهد شد. در این مورد نیز میتوانید از DelegatingHandlerها استفاده کنید. معرفی DelegatingHandler طراحی شده به Asp.Net PipeLine به صورت زیر خواهد بود:
public class Startup { public void Configuration( IAppBuilder appBuilder ) { var config = new HttpConfiguration(); var routeHandler = HttpClientFactory.CreatePipeline( new HttpControllerDispatcher( config ), new DelegatingHandler[] { new MyHandler(), } ); config.Routes.MapHttpRoute( name: "Default", routeTemplate: "{controller}/{action}", defaults: null, constraints: null, handler: routeHandler ); config.EnsureInitialized(); appBuilder.UseWebApi( config ); } }
public class IpMiddleware : OwinMiddleware { private readonly HashSet<string> _deniedIps; public IpMiddleware(OwinMiddleware next, HashSet<string> deniedIps) : base(next) { _deniedIps = deniedIps; } public override async Task Invoke(OwinRequest request, OwinResponse response) { var ipAddress = (string)request.Environment["server.RemoteIpAddress"]; if (_deniedIps.Contains(ipAddress)) { response.StatusCode = 403; return; } await Next.Invoke(request, response); } }
در نهایت برای معرفی این Middleware طراحی شده به Application، مراحل زیر را انجام دهید.
public class Startup { public void Configuration( IAppBuilder appBuilder ) { var config = new HttpConfiguration(); var deniedIps = new HashSet<string> {"192.168.0.100", "192.168.0.101"};
app.Use(typeof(IpMiddleware), deniedIps); appBuilder.UseWebApi( config ); } }