ASP.NET MVC #1
چون با اجرای چند مثال که انجام دادم به نظر در اجرای اولیه در MVC ویوها یا همان صفحات نهایی با تاخیر ایجاد و نمایش داده میشوند
آیا این فقط در اجرای اولیه سایت است و کاربران دیگر با این تاخیر مواجه نمیشن ؟ یا هر کاربر در ورود اولیه خود به سایت با این تاخیرها در نمایش مواجه است ؟
فعالسازی Windows Authentication در IIS
پس از publish برنامه و رعایت مواردی که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS» بحث شد، باید به قسمت Authentication برنامهی مدنظر، در کنسول مدیریتی IIS رجوع کرد:
و سپس Windows Authentication را با کلیک راست بر روی آن و انتخاب گزینهی Enable، فعال نمود:
این تنظیم دقیقا معادل افزودن تنظیمات ذیل به فایل web.config برنامه است:
<system.webServer> <security> <authentication> <anonymousAuthentication enabled="true" /> <windowsAuthentication enabled="true" /> </authentication> </security> </system.webServer>
فعالسازی Windows Authentication در IIS Express
اگر برای آزمایش میخواهید از IIS Express به همراه ویژوال استودیو استفاده کنید، نیاز است فایلی را به نام Properties\launchSettings.json با محتوای ذیل در ریشهی پروژهی خود ایجاد کنید (و یا تغییر دهید):
{ "iisSettings": { "windowsAuthentication": true, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3381/", "sslPort": 0 } } }
تغییر مهم فایل web.config برنامه جهت هدایت اطلاعات ویندوز به آن
اگر پروژهی شما فایل web.config ندارد، باید آنرا اضافه کنید؛ با حداقل محتوای ذیل:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> </handlers> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true"/> </system.webServer> </configuration>
تنظیمات برنامهی ASP.NET Core جهت فعالسازی Windows Authentication
پس از فعالسازی windowsAuthentication در IIS و همچنین تنظیم forwardWindowsAuthToken به true در فایل web.config برنامه، اکنون جهت استفادهی از windowsAuthentication دو روش وجود دارد:
الف) تنظیمات مخصوص برنامههای Self host
اگر برنامهی وب شما قرار است به صورت self host ارائه شود (بدون استفاده از IIS)، تنها کافی است در تنظیمات ابتدای برنامه در فایل Program.cs، استفادهی از میانافزار HttpSys را ذکر کنید:
namespace ASPNETCore2WindowsAuthentication { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .UseHttpSys(options => // Just for local tests without IIS, Or self-hosted scenarios on Windows ... { options.Authentication.Schemes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM; options.Authentication.AllowAnonymous = true; //options.UrlPrefixes.Add("http://+:80/"); }) .Build(); host.Run(); } } }
ب) تنظیمات مخصوص برنامههایی که قرار است در IIS هاست شوند
در اینحالت تنها کافی است UseIISIntegration در تنظیمات ابتدایی برنامه ذکر شود و همانطور که عنوان شد، نیازی به UseHttpSys در این حالت نیست:
namespace ASPNETCore2WindowsAuthentication { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }) .UseStartup<Startup>() .Build(); host.Run(); } } }
فعالسازی میانافزار اعتبارسنجی ASP.NET Core جهت یکپارچه شدن با Windows Authentication
در پایان تنظیمات فعالسازی Windows Authentication نیاز است به فایل Startup.cs برنامه مراجعه کرد و یکبار AddAuthentication را به همراه تنظیم ChallengeScheme آن به IISDefaults افزود:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure<IISOptions>(options => { // Sets the HttpContext.User // Note: Windows Authentication must also be enabled in IIS for this to work. options.AutomaticAuthentication = true; options.ForwardClientCertificate = true; }); services.AddAuthentication(options => { // for both windows and anonymous authentication options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme; }); }
آزمایش برنامه با تدارک یک کنترلر محافظت شده
در اینجا قصد داریم اطلاعات ذیل را توسط تعدادی اکشن متد، نمایش دهیم:
private string authInfo() { var claims = new StringBuilder(); if (User.Identity is ClaimsIdentity claimsIdentity) { claims.Append("Your claims: \n"); foreach (var claim in claimsIdentity.Claims) { claims.Append(claim.Type + ", "); claims.Append(claim.Value + "\n"); } } return $"IsAuthenticated: {User.Identity.IsAuthenticated}; Identity.Name: {User.Identity.Name}; WindowsPrincipal: {(User is WindowsPrincipal)}\n{claims}"; }
namespace ASPNETCore2WindowsAuthentication.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } [Authorize] public IActionResult Windows() { return Content(authInfo()); } private string authInfo() { var claims = new StringBuilder(); if (User.Identity is ClaimsIdentity claimsIdentity) { claims.Append("Your claims: \n"); foreach (var claim in claimsIdentity.Claims) { claims.Append(claim.Type + ", "); claims.Append(claim.Value + "\n"); } } return $"IsAuthenticated: {User.Identity.IsAuthenticated}; Identity.Name: {User.Identity.Name}; WindowsPrincipal: {(User is WindowsPrincipal)}\n{claims}"; } [AllowAnonymous] public IActionResult Anonymous() { return Content(authInfo()); } [Authorize(Roles = "Domain Admins")] public IActionResult ForAdmins() { return Content(authInfo()); } [Authorize(Roles = "Domain Users")] public IActionResult ForUsers() { return Content(authInfo()); } } }
dotnet publish
اکنون اگر برنامه را در مرورگر مشاهده کنیم، یک چنین خروجی قابل دریافت است:
در اینجا نام کاربر وارد شدهی به ویندوز و همچنین لیست تمام Claims او مشاهده میشوند. مسیر Home/Windows نیز توسط ویژگی Authorize محافظت شدهاست.
برای محدود کردن دسترسی کاربران به اکشن متدها، توسط گروههای دومین و اکتیودایرکتوری، میتوان به نحو ذیل عمل کرد:
[Authorize(Roles = @"<domain>\<group>")] //or [Authorize(Roles = @"<domain>\<group1>,<domain>\<group2>")]
services.AddAuthorization(options => { options.AddPolicy("RequireWindowsGroupMembership", policy => { policy.RequireAuthenticatedUser(); policy.RequireRole(@"<domain>\<group>")); } });
[Authorize(Policy = "RequireWindowsGroupMembership")]
[HttpGet("[action]")] public IActionResult SomeValue() { if (!User.IsInRole(@"Domain\Group")) return StatusCode(403); return Ok("Some Value"); }
افزودن Claims سفارشی به Claims پیشفرض کاربر سیستم
همانطور که در شکل فوق ملاحظه میکنید، یک سری Claims حاصل از Windows Authentication در اینجا به شیء User اضافه شدهاند؛ بدون اینکه برنامه، صفحهی لاگینی داشته باشد و همینقدر که کاربر به ویندوز وارد شدهاست، میتواند از برنامه استفاده کند.
اگر نیاز باشد تا Claims خاصی به لیست Claims کاربر جاری اضافه شود، میتوان از پیاده سازی یک IClaimsTransformation سفارشی استفاده کرد:
public class ApplicationClaimsTransformation : IClaimsTransformation { private readonly ILogger<ApplicationClaimsTransformation> _logger; public ApplicationClaimsTransformation(ILogger<ApplicationClaimsTransformation> logger) { _logger = logger; } public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { if (!(principal.Identity is ClaimsIdentity identity)) { return Task.FromResult(principal); } var claims = addExistingUserClaims(identity); identity.AddClaims(claims); return Task.FromResult(principal); } private IEnumerable<Claim> addExistingUserClaims(IIdentity identity) { var claims = new List<Claim>(); var user = @"VahidPC\Vahid"; if (identity.Name != user) { _logger.LogError($"Couldn't find {identity.Name}."); return claims; } claims.Add(new Claim(ClaimTypes.GivenName, user)); return claims; } }
services.AddScoped<IClaimsTransformation, ApplicationClaimsTransformation>(); services.AddAuthentication(options => { // for both windows and anonymous authentication options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme; });
به این ترتیب میتوان لیست Claims ثبت شدهی یک کاربر را در یک بانک اطلاعاتی استخراج و به لیست Claims فعلی آن افزود و دسترسیهای بیشتری را به او اعطاء کرد (فراتر از دسترسیهای پیشفرض سیستم عامل).
برای دسترسی به مقادیر این Claims نیز میتوان به صورت ذیل عمل کرد:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var userName = User.FindFirstValue(ClaimTypes.Name); var userName = User.FindFirstValue(ClaimTypes.GivenName);
کدهای کامل این برنامه را از اینجا میتوانید دریافت کنید: ASPNETCore2WindowsAuthentication.zip
تزریق وابستگیهای AutoMapper در لایه سرویس برنامه
این «مثال» اساسا یک پروژه بیشتر نبود؛ صرفا جهت نمایش مفهوم مورد بحث. در همین «مثال» تعاریف نگاشتها داخل پوشهی سرویس نیست.
در کل میتوانید یک اسمبلی جداگانه برای آن درنظر بگیرید به نام مثلا AutoMapperConfig. تنها قسمت مهم آن، بارگذاری و خواندن این نگاشتها در زمان آغاز برنامه است که در مثال جاری، اینکار توسط SmObjectFactory به صورت خودکار انجام میشود.
در کل هدف از اکثر مثالهای این سایت یا سایتهای مشابه دیگر، رساندن یک مفهوم است؛ نه ارائهی یک راه حل جامع و مانع. همینقدر که مثال زده شده، عنوان مورد بحث را پوشش دهد، کافی است.
استفاده از چند فرم در کنار هم در ASP.NET MVC
در مثال بالا نیازی به استفاده از Html.Action و child action نبوده. از Html.Action زمانی استفاده میشه که قراره چند partial view از پیش مقدار دهی شده در صفحه، از منابع داده مختلفی مقدار دهی بشن. مثلا قراره در کنار صفحه، از یک منبع داده، آمار سایت رو نمایش بدید (اطلاعات از پیش مقدار دهی شده) و از یک منبع دیگه، لیست مطالب و این دو مورد هم قراره در layout سایت قرار بگیرن. اینجا شما ویرایش اطلاعات رو که ندارید (برای مقدار دهی عناصر فرم از قبل). فقط قراره از کاربر اطلاعات بگیرید. بنابراین استفاده از رندر کردن پارشال مثل قبل کار میکنه. مهم هم نیست که هر پارشالی مدل مختلفی داره. برای ارسال اطلاعات فرم به سرور، فقط نام کنترلها مهم است و نه نوع مدل شما. پروتکل HTTP چیزی از نوع مدل نمیدونه. ASP.NET MVC هست که نامها رو میگیره و متصل میکنه به خواص یک شیء.
در کل مثال بالا برای حالت قرار دادن این فرمها در layout سایت برای تمام صفحات طراحی شده. اگر یک صفحه معمولی و view معمولی هست، در ASP.NET MVC مجاز هستید n تا فرم در صفحه قرار بدید. برای اینکار نیازی به Html.Action نیست؛ با رندر پارشال هم کار میکنه. برای تامین داده اونها هم از ViewModel استفاده کنید. این ViewModel یک خاصیتش، کل اطلاعات فرم یک هست و خاصیت دومش کل اطلاعات فرم 2 و الی آخر.
پیش نمایش Rider 2019.1
چگونه کدها را مستند سازی کنیم؟
معرفی پروژه فروشگاهی Iris Store
EF Code First #4
اندرباب اهمیت به اشتراک گذاری اطلاعات
جوابتون رو هم میتونید اینجا پیدا کنید: (^)