Globalization در ASP.NET MVC
ضمن تشکر از مطلب بسیار خوبتون
خاصیت DisplayFormat قابلیت استفاده از کلیدهای Resource را ندارد!
لطفا راهنمایی فرمایید که چطور میشه از این خاصیت برای DisplayFormat استفاده کرد؟
من میخوام برای تاریخ در زبانه فارسی از فرمت {yyyy-MM-dd} و در زبانه انگلیسی از {yyyy-dd-MM} استفاده کنم.
با سپاس فراوان
از کدام یک از برنامههای issue tracker استفاده کردهاید؟
استفاده از Froala WYSIWYG Editor در ASP.NET
return Json(new { link = "Content/Images/" + fileName }, JsonRequestBehavior.AllowGet);
http://localhost:1455/Content/Images/a_sunny_days_end-wallpaper-1440x900.jpg
OpenCVSharp #1
پردازش تصاویر علمی است برای پیاده سازی الگوریتمهای مختلفی بر روی تصاویر دیجیتال؛ برای مثال تشخیص خودکار شمارهی پلاک خودروهای وارد شدهی به محدودهی طرح ترافیک، تا تشخیص چهرهی افراد، در گوشیهای همراه. پردازش تصاویر، در صنایع مختلف، علوم پزشکی و همچنین نظامی، کاربردهای بسیاری دارند.
برای انجام این کار، کتابخانههای بسیار زیادی طراحی شدهاند؛ اما در این بین OpenCV جایگاه خاصی دارد. این کتابخانهی بسیار مشهور سورس باز، جهت پردازش تصاویر در سیستم عاملهای مختلفی مانند Windows, Mac, Linux, Android و iOS بکار میرود.
محصور کنندههای OpenCV مخصوص دات نت
تا امروز محصور کنندههای زیادی جهت استفادهی از کتابخانهی OpenCV در دات نت طراحی شدهاند که تعدادی از مهمترینهای آنها به شرح زیر هستند:
الف) Emgu CV
این کتابخانه، یکی از مشهورترین محصور کنندههای OpenCV است و دارای مجوزی دوگانه میباشد. برای کارهای سورس باز، مجوز GPL دارد (یعنی باید کارتان را سورس باز کنید) و برای کارهای تجاری باید مجوز آنرا بخرید. البته باید توجه داشت که مجوز کتابخانهی اصلی OpenCV از نوع BSD است و این محدودیتها را ندارد.
ب) OpenCvSharp
کتابخانهی OpenCvSharp دارای مجوز BSD است (همانند کتابخانهی اصلی OpenCV) و محدودیتی برای استفاده ندارد. هر دو نوع مدل برنامه نویسی OpenCV را که شامل متدهای C و ++C آناست، پشتیبانی میکند و در طراحی آن سعی شدهاست که بیشترین نزدیکی به طراحی اصلی OpenCV وجود داشته باشد. همچنین این کتابخانه چندسکویی بوده و با Mono لینوکسی نیز سازگار است و از دات نت 2 به بعد را نیز پشتیبانی میکند. جامعهی کاربری آن فعال است و مدام به روز میشود.
ج) SharperCV
دیگر نگهداری نمیشود.
د) OpenCVDotNet
آخرین تاریخ به روز رسانی آن سال 2007 است.
ه) DirectCV
آخرین تاریخ به روز رسانی آن سال 2011 است.
در این بین یکی از بهترین انتخابها، کتابخانهی OpenCvSharp ژاپنی است. مجوز استفادهی از آن محدود نیست. به روز رسانی مرتب و منظمی دارد و API آن طوری طراحی شدهاست که به سادگی بتوانید مثالهای C و ++C کتابخانهی OpenCV را تبدیل به معادلهای #C کنید.
نصب OpenCvSharp
برای نصب کتابخانهی OpenCvSharp میتوان از بستههای نیوگت آن کمک گرفت. این کتابخانه به همراه دو بستهی نیوگت ارائه میشود.
اگر فرمان ذیل را صادر کنید
PM> Install-Package OpenCvSharp-AnyCPU
و اگر دستور ذیل را اجرا کنید:
PM> Install-Package OpenCvSharp-WithoutDll
روش توصیه شدهی در اینجا، همان نصب بستهی نیوگت OpenCvSharp-AnyCPU است. به این ترتیب نگارشهای X86 و X64 کتابخانهی OpenCV سازگار با OpenCvSharp را نیز دریافت خواهید کرد.
نکتهای در مورد ارائهی نهایی پروژههای مبتنی بر OpenCV
OpenCV یک کتابخانهی native ویندوز است و دات نتی نیست . بنابراین DLLهای آن باید بسته به معماری CPU جاری، انتخاب شوند. یعنی اگر برنامهی دات نتی خود را در حالت Any CPU کامپایل میکنید، این برنامه در یک سیستم 64 بیتی، 64 بیتی رفتار میکند و در یک سیستم 32 بیتی، 32 بیتی. بنابراین باید دقت داشت که اگر سیستم جاری 64 بیتی است و میخواهید از اسمبلیهای X86 مربوط به OpenCV استفاده کنید، برنامه با پیام استثنای یافت نشدن OpenCV و BadImageFormatException کرش خواهد کرد. بستهی نیوگت OpenCvSharp-AnyCPU شامل هر دو معماری X86 و X64 است و هر دو سری DLLهای OpenCV را به همراه دارد.
همچنین OpenCV تحت ویندوز، توسط کامپایلر ویژوال ++C، کامپایل شدهاست. به همین جهت در این حالت، علاوه بر نصب دات نت، نیاز است VC++ redistributable packages را نیز بر روی کامپیوتر کلاینت نصب کرد.
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU اگر به پوشهی bin برنامهی خود مراجعه کنید، پوشهی جدید dll را نیز میتوان مشاهده کرد. داخل این پوشه، دو پوشهی X86 و X64 وجود دارند که حاوی DLLهای اصلی OpenCV میباشند. در این پوشهها اگر برای مثال فایلی به نام msvcp120.dll را یافتید، یعنی این نگارش از OpenCV نیاز به بستههای مخصوص VC++ 12 دارد.
رعایت این دو نکته بسیار مهم است؛ در غیر اینصورت برنامهی شما آغاز نخواهد شد.
اولین برنامهی OpenCVSharp
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU، مقدمات نصب OpenCV به پایان میرسد. در ادامه یک برنامهی کنسول جدید را ایجاد کرده و کدهای ذیل را به آن اضافه کنید:
using OpenCvSharp; namespace OpenCVSharpSample01 { class Program { static void Main(string[] args) { var img = Cv.CreateImage(new CvSize(128, 128), BitDepth.U8, 1); for (var y = 0; y < img.Height; y++) { for (var x = 0; x < img.Width; x++) { Cv.Set2D(img, y, x, x + y); } } Cv.NamedWindow("window"); Cv.ShowImage("window", img); Cv.WaitKey(); Cv.DestroyWindow("window"); Cv.ReleaseImage(img); } } }
در این مثال یک تصویر 128*128 ایجاد شده و سپس با گرادیانی از رنگ خاکستری پر میشود. در ادامه یک پنجرهی native مخصوص OpenCV ایجاد شده و این تصویر در آن نمایش داده میشود.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
در این حالت هش کردن کلمات عبور ایدهی بهتر است. هشها روشهایی یک طرفه هستند که با داشتن نتیجهی نهایی آنها، نمیتوان به اصل کلمهی عبور مورد استفاده دسترسی پیدا کرد. برای بهبود امنیت هشهای تولیدی، میتوان از مفهومی به نام Salt نیز استفاده نمود. Salt در اصل یک رشتهی تصادفی است که پیش از هش شدن نهایی کلمهی عبور، به آن اضافه شده و سپس حاصل این جمع، هش خواهد شد. اهمیت این مساله در بالا بردن زمان یافتن کلمهی عبور اصلی از روی هش نهایی است (توسط روشهایی مانند brute force یا امتحان کردن بازهی وسیعی از عبارات قابل تصور).
اما واقعیت این است که حتی استفاده از یک Salt نیز نمیتواند امنیت بازیابی کلمات عبور هش شده را تضمین کند. برای مثال نرم افزارهایی موجود هستند که با استفاده از پرداش موازی قادرند بیش از 60 میلیارد هش را در یک ثانیه آزمایش کنند و البته این کارآیی، برای کار با هشهای متداولی مانند MD5 و SHA1 بهینه سازی شدهاست.
روش هش کردن کلمات عبور در ASP.NET Identity
ASP.NET Identity 2.x که در حال حاضر آخرین نگارش تکامل یافتهی روشهای امنیتی توصیه شدهی توسط مایکروسافت، برای برنامههای وب است، از استانداردی به نام RFC 2898 و الگوریتم PKDBF2 برای هش کردن کلمات عبور استفاده میکند. مهمترین مزیت این روش خاص، کندتر شدن الگوریتم آن با بالا رفتن تعداد سعیهای ممکن است؛ برخلاف الگوریتمهایی مانند MD5 یا SHA1 که اساسا برای رسیدن به نتیجه، در کمترین زمان ممکن طراحی شدهاند.
PBKDF2 یا password-based key derivation function جزئی از استاندارد RSA نیز هست (PKCS #5 version 2.0). در این الگوریتم، تعداد بار تکرار، یک Salt و یک کلمهی عبور تصادفی جهت بالا بردن انتروپی (بینظمی) کلمهی عبور اصلی، به آن اضافه میشوند. از تعداد بار تکرار برای تکرار الگوریتم هش کردن اطلاعات، به تعداد باری که مشخص شدهاست، استفاده میگردد. همین تکرار است که سبب کندشدن محاسبهی هش میگردد. عدد معمولی که برای این حالت توصیه شدهاست، 50 هزار است.
این استاندارد در دات نت توسط کلاس Rfc2898DeriveBytes پیاده سازی شدهاست که در ذیل مثالی را در مورد نحوهی استفادهی عمومی از آن، مشاهده میکنید:
using System; using System.Diagnostics; using System.Security.Cryptography; using System.Text; namespace IdentityHash { public static class PBKDF2 { public static byte[] GenerateSalt() { using (var randomNumberGenerator = new RNGCryptoServiceProvider()) { var randomNumber = new byte[32]; randomNumberGenerator.GetBytes(randomNumber); return randomNumber; } } public static byte[] HashPassword(byte[] toBeHashed, byte[] salt, int numberOfRounds) { using (var rfc2898 = new Rfc2898DeriveBytes(toBeHashed, salt, numberOfRounds)) { return rfc2898.GetBytes(32); } } } class Program { static void Main(string[] args) { var passwordToHash = "VeryComplexPassword"; hashPassword(passwordToHash, 50000); Console.ReadLine(); } private static void hashPassword(string passwordToHash, int numberOfRounds) { var sw = new Stopwatch(); sw.Start(); var hashedPassword = PBKDF2.HashPassword( Encoding.UTF8.GetBytes(passwordToHash), PBKDF2.GenerateSalt(), numberOfRounds); sw.Stop(); Console.WriteLine(); Console.WriteLine("Password to hash : {0}", passwordToHash); Console.WriteLine("Hashed Password : {0}", Convert.ToBase64String(hashedPassword)); Console.WriteLine("Iterations <{0}> Elapsed Time : {1}ms", numberOfRounds, sw.ElapsedMilliseconds); } } }
پیش فرضهای پیاده سازی Rfc2898DeriveBytes استفاده از الگوریتم SHA1 با 1000 بار تکرار است؛ چیزی که دقیقا در ASP.NET Identity 2.x بکار رفتهاست.
تفاوتهای الگوریتمهای هش کردن اطلاعات در نگارشهای مختلف ASP.NET Identity
اگر به سورس نگارش سوم ASP.NET Identity مراجعه کنیم، یک چنین کامنتی در ابتدای آن قابل مشاهده است:
/* ======================= * HASHED PASSWORD FORMATS * ======================= * * Version 2: * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. * (See also: SDL crypto guidelines v5.1, Part III) * Format: { 0x00, salt, subkey } * * Version 3: * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations. * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } * (All UInt32s are stored big-endian.) */
در یک چنین حالتی بانک اطلاعاتی ASP.NET Identity 2.x شما با نگارش بعدی سازگار نخواهد بود و تمام کلمات عبور آن باید مجددا ریست شده و مطابق فرمت جدید هش شوند. بنابراین امکان انتخاب الگوریتم هش کردن را نیز پیش بینی کردهاند.
در نگارش دوم ASP.NET Identity، متد هش کردن یک کلمهی عبور، چنین شکلی را دارد:
public static string HashPassword(string password, int numberOfRounds = 1000) { if (password == null) throw new ArgumentNullException("password"); byte[] saltBytes; byte[] hashedPasswordBytes; using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, numberOfRounds)) { saltBytes = rfc2898DeriveBytes.Salt; hashedPasswordBytes = rfc2898DeriveBytes.GetBytes(32); } var outArray = new byte[49]; Buffer.BlockCopy(saltBytes, 0, outArray, 1, 16); Buffer.BlockCopy(hashedPasswordBytes, 0, outArray, 17, 32); return Convert.ToBase64String(outArray); }
جایی برای استاندارد سازی وب
Blogger auto poster
کلیدهای مربوط به Request
ضروری؟ | نام کلید | مقدار |
بله | "owin.RequestBody" | یک Stream همراه با request body. اگر body برای request وجود نداشته باشد، Stream.Null به عنوان placeholder قابل استفاده است. |
بله | "owin.RequestHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای درخواست. |
بله | "owin.RequestMethod" | رشتهایی حاوی نوع فعل متد HTTP مربوط به درخواست (مانند GET and POST ) |
بله | "owin.RequestPath" | path درخواست شده به صورت string |
بله | "owin.RequestPathBase" | قسمتی از path درخواست به صورت string |
بله | "owin.RequestProtocol" | نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 ) |
بله | "owin.RequestQueryString" | رشتهای حاوی query string ؛ بدون علامت ? (مانند foo=bar&baz=quux ) |
بله | "owin.RequestScheme" | رشتهایی حاوی URL scheme استفاده شده در درخواست (مانند HTTP or HTTPS ) |
ضروری؟ | نام کلید | مقدار |
بله | "owin.ResponseBody" | یک Stream جهت نوشتن response body در خروجی |
بله | "owin.ResponseHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای response |
خیر | "owin.ResponseStatusCode" | یک عدد صحیح حاوی کد وضعیت HTTP response ؛ حالت پیشفرض 200 است. |
خیر | "owin.ResponseReasonPhrase" | یک رشته حاوی reason phrase مربوط به status code ؛ اگر خالی باشد در نتیجه سرور بهتر است آن را مقداردهی کند. |
خیر | "owin.ResponseProtocol" | یک رشته حاوی نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 )؛ اگر خالی باشد؛ “owin.RequestProtocol” به عنوان مقدار پیشفرض در نظر گرفته خواهد شد. |
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net461" /> <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net461" /> <package id="Owin" version="1.0" targetFramework="net461" />
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { } } }
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { app.Use(async (ctx, next) => { await ctx.Response.WriteAsync("Hello"); }); } }
Func<IOwinContext, Func<Task>, Task> handler
app.Use(async (ctx, next) => { var response = ctx.Environment["owin.ResponseBody"] as Stream; using (var writer = new StreamWriter(response)) { await writer.WriteAsync("Hello"); } });
using System; using Microsoft.Owin.Hosting; namespace SimpleOwinConsoleApp { class Program { static void Main(string[] args) { using (WebApp.Start<Startup>("http://localhost:12345")) { Console.WriteLine("Listening to port 12345"); Console.WriteLine("Press Enter to end..."); Console.ReadLine(); } } } }
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace SimpleOwinCoreApp { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinCoreApp.Middlewares { public class SimpleMiddleware { private readonly RequestDelegate _next; public SimpleMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext ctx) { // قبل از فراخوانی میانافزار بعدی await ctx.Response.WriteAsync("Hello DNT!"); await _next(ctx); // بعد از فراخوانی میانافزار بعدی } } }
app.UseMiddleware<SimpleMiddleware>();
"Microsoft.AspNetCore.Owin": "1.0.0"
app.UseOwin(pipeline => { pipeline(next => new MyKatanaBasedMiddleware(next).Invoke) });
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerMiddleware { private readonly RequestDelegate _next; private readonly IpBlockerOptions _options; public IpBlockerMiddleware(RequestDelegate next, IpBlockerOptions options) { _next = next; _options = options; } public async Task Invoke(HttpContext context) { var ipAddress = context.Request.Host.Host; if (IsBlockedIpAddress(ipAddress)) { context.Response.StatusCode = 403; await context.Response.WriteAsync("Forbidden : The server understood the request, but It is refusing to fulfill it."); return; } await _next.Invoke(context); } private bool IsBlockedIpAddress(string ipAddress) { return _options.Ips.Any(ip => ip == ipAddress); } } }
using System.Collections.Generic; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerOptions { public IpBlockerOptions() { Ips = new[] { "192.168.1.1" }; } public IList<string> Ips { get; set; } } }
using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; namespace SimpleOwinAspNetCore.Middleware { public static class IpBlockerExtensions { public static IApplicationBuilder UseIpBlocker(this IApplicationBuilder builder, IConfigurationRoot configuration, IpBlockerOptions options = null) { return builder.UseMiddleware<IpBlockerMiddleware>(options ?? new IpBlockerOptions { Ips = configuration.GetSection("block_list").GetChildren().Select(p => p.Value).ToArray() }); } } }
{ "block_list": [ "192.168.1.1", "localhost", "127.0.0.1", "172.16.132.151" ] }
public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("blockedIps.json"); Configuration = builder.Build(); }
app.UseIpBlocker(Configuration);