سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سیزده دقیقه

ASP.NET Core Identity به همراه دو قابلیت جدید است که پیاده سازی سطوح دسترسی پویا را با سهولت بیشتری میسر می‌کند:
الف) Policies
ب) Role Claims


سیاست‌های دسترسی یا Policies در ASP.NET Core Identity

ASP.NET Core Identity هنوز هم از مفهوم Roles پشتیبانی می‌کند. برای مثال می‌توان مشخص کرد که اکشن متدی و یا تمام اکشن متدهای یک کنترلر تنها توسط کاربران دارای نقش Admin قابل دسترسی باشند. اما نقش‌ها نیز در این سیستم جدید تنها نوعی از سیاست‌های دسترسی هستند.
[Authorize(Roles = ConstantRoles.Admin)]
public class RolesManagerController : Controller
برای مثال در اینجا دسترسی به امکانات مدیریت نقش‌های سیستم، به نقش ثابت و از پیش تعیین شده‌ی Admin منحصر شده‌است و تمام کاربرانی که این نقش به آن‌ها انتساب داده شود، امکان استفاده‌ی از این قابلیت‌ها را خواهند یافت.
اما نقش‌های ثابت، بسیار محدود و غیر قابل انعطاف هستند. برای رفع این مشکل مفهوم جدیدی را به نام Policy اضافه کرده‌اند.
[Authorize(Policy="RequireAdministratorRole")]
public IActionResult Get()
{
   /* .. */
}
سیاست‌های دسترسی بر اساس Requirements و یا نیازهای سیستم تعیین می‌شوند و تعیین نقش‌ها، تنها یکی از قابلیت‌های آن‌ها هستند.
برای مثال اگر بخواهیم تک نقش Admin را به صورت یک سیاست دسترسی جدید تعریف کنیم، روش کار به صورت ذیل خواهد بود:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Admin"));
    });
}
در تنظیمات متد AddAuthorization، یک سیاست دسترسی جدید تعریف شده‌است که جهت برآورده شدن نیازمندی‌های آن، کاربر سیستم باید دارای نقش Admin باشد که نمونه‌ای از نحوه‌ی استفاده‌ی از آن‌را با ذکر [Authorize(Policy="RequireAdministratorRole")] ملاحظه کردید.
و یا بجای اینکه چند نقش مجاز به دسترسی منبعی را با کاما از هم جدا کنیم:
 [Authorize(Roles = "Administrator, PowerUser, BackupAdministrator")]
می‌توان یک سیاست دسترسی جدید را به نحو ذیل تعریف کرد که شامل تمام نقش‌های مورد نیاز باشد و سپس بجای ذکر Roles، از نام این Policy جدید استفاده کرد:
options.AddPolicy("ElevatedRights", policy => policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
به این صورت
[Authorize(Policy = "ElevatedRights")]
public IActionResult Shutdown()
{
   return View();
}

سیاست‌های دسترسی تنها به نقش‌ها محدود نیستند:
services.AddAuthorization(options =>
{
   options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
برای مثال می‌توان دسترسی به یک منبع را بر اساس User Claims یک کاربر به نحوی که ملاحظه می‌کنید، محدود کرد:
[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
   return View();
}


سیاست‌های دسترسی پویا در ASP.NET Core Identity

مهم‌ترین مزیت کار با سیاست‌های دسترسی، امکان سفارشی سازی و تهیه‌ی نمونه‌های پویای آن‌ها هستند؛ موردی که با نقش‌های ثابت سیستم قابل پیاده سازی نبوده و در نگارش‌های قبلی، جهت پویا سازی آن، یکی از روش‌های بسیار متداول، تهیه‌ی فیلتر Authorize سفارشی سازی شده بود. اما در اینجا دیگر نیازی نیست تا فیلتر Authorize را سفارشی سازی کنیم. با پیاده سازی یک AuthorizationHandler جدید و معرفی آن به سیستم، پردازش سیاست‌های دسترسی پویای به منابع، فعال می‌شود.
پیاده سازی سیاست‌های پویای دسترسی شامل مراحل ذیل است:
1- تعریف یک نیازمندی دسترسی جدید
public class DynamicPermissionRequirement : IAuthorizationRequirement
{
}
ابتدا باید یک نیازمندی دسترسی جدید را با پیاده سازی اینترفیس IAuthorizationRequirement ارائه دهیم. این نیازمندی مانند روشی که در پروژه‌ی DNT Identity بکار گرفته شده‌است، خالی است و صرفا به عنوان نشانه‌ای جهت یافت AuthorizationHandler استفاده کننده‌ی از آن استفاده می‌شود. در اینجا در صورت نیاز می‌توان یک سری خاصیت اضافه را تعریف کرد تا آن‌ها را به صورت پارامترهایی ثابت به AuthorizationHandler ارسال کند.

2- پیاده سازی یک AuthorizationHandler استفاده کننده‌ی از نیازمندی دسترسی تعریف شده
پس از اینکه نیازمندی DynamicPermissionRequirement را تعریف کردیم، در ادامه باید یک AuthorizationHandler استفاده کننده‌ی از آن را تعریف کنیم:
    public class DynamicPermissionsAuthorizationHandler : AuthorizationHandler<DynamicPermissionRequirement>
    {
        private readonly ISecurityTrimmingService _securityTrimmingService;

        public DynamicPermissionsAuthorizationHandler(ISecurityTrimmingService securityTrimmingService)
        {
            _securityTrimmingService = securityTrimmingService;
            _securityTrimmingService.CheckArgumentIsNull(nameof(_securityTrimmingService));
        }

        protected override Task HandleRequirementAsync(
             AuthorizationHandlerContext context,
             DynamicPermissionRequirement requirement)
        {
            var mvcContext = context.Resource as AuthorizationFilterContext;
            if (mvcContext == null)
            {
                return Task.CompletedTask;
            }

            var actionDescriptor = mvcContext.ActionDescriptor;
            var area = actionDescriptor.RouteValues["area"];
            var controller = actionDescriptor.RouteValues["controller"];
            var action = actionDescriptor.RouteValues["action"];

            if(_securityTrimmingService.CanCurrentUserAccess(area, controller, action))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }

            return Task.CompletedTask;
        }
    }
کار با ارث بری از AuthorizationHandler شروع شده و آرگومان جنریک آن، همان نیازمندی است که پیشتر تعریف کردیم. از این آرگومان جنریک جهت یافتن خودکار AuthorizationHandler متناظر با آن، توسط ASP.NET Core Identity استفاده می‌شود. بنابراین در اینجا DynamicPermissionRequirement تهیه شده صرفا کارکرد علامتگذاری را دارد.
در کلاس تهیه شده باید متد HandleRequirementAsync آن‌را بازنویسی کرد و اگر در این بین، منطق سفارشی ما context.Succeed را فراخوانی کند، به معنای برآورده شدن سیاست دسترسی بوده و کاربر جاری می‌تواند به منبع درخواستی، بلافاصله دسترسی یابد و اگر context.Fail فراخوانی شود، در همینجا دسترسی کاربر قطع شده و HTTP status code مساوی 401 (عدم دسترسی) را دریافت می‌کند.

منطق سفارشی پیاده سازی شده نیز به این صورت است:
نام ناحیه، کنترلر و اکشن متد درخواستی کاربر از مسیریابی جاری استخراج می‌شوند. سپس توسط سرویس سفارشی ISecurityTrimmingService تهیه شده، بررسی می‌کنیم که آیا کاربر جاری به این سه مؤلفه دسترسی دارد یا خیر؟

3- معرفی سیاست دسترسی پویای تهیه شده به سیستم
معرفی سیاست کاری پویا و سفارشی تهیه شده، شامل دو مرحله‌ی زیر است:
        private static void addDynamicPermissionsPolicy(this IServiceCollection services)
        {
            services.AddScoped<IAuthorizationHandler, DynamicPermissionsAuthorizationHandler>();
            services.AddAuthorization(opts =>
            {
                opts.AddPolicy(
                    name: ConstantPolicies.DynamicPermission,
                    configurePolicy: policy =>
                    {
                        policy.RequireAuthenticatedUser();
                        policy.Requirements.Add(new DynamicPermissionRequirement());
                    });
            });
        }
ابتدا باید DynamicPermissionsAuthorizationHandler تهیه شده را به سیستم تزریق وابستگی‌ها معرفی کنیم.
سپس یک Policy جدید را با نام دلخواه DynamicPermission تعریف کرده و نیازمندی علامتگذار خود را به عنوان یک policy.Requirements جدید، اضافه می‌کنیم. همانطور که ملاحظه می‌کنید یک وهله‌ی جدید از DynamicPermissionRequirement در اینجا ثبت شده‌است. همین وهله به متد HandleRequirementAsync نیز ارسال می‌شود. بنابراین اگر نیاز به ارسال پارامترهای بیشتری به این متد وجود داشت، می‌توان خواص مرتبطی را به کلاس DynamicPermissionRequirement نیز اضافه کرد.
همانطور که مشخص است، در اینجا یک نیازمندی را می‌توان ثبت کرد و نه Handler آن‌را. این Handler از سیستم تزریق وابستگی‌ها، بر اساس آرگومان جنریک AuthorizationHandler پیاده سازی شده، به صورت خودکار یافت شده و اجرا می‌شود (بنابراین اگر Handler شما اجرا نشد، مطمئن شوید که حتما آن‌را به سیستم تزریق وابستگی‌ها معرفی کرده‌اید).

پس از آن هر کنترلر یا اکشن متدی که از این سیاست دسترسی پویای تهیه شده استفاده کند:
[Authorize(Policy = ConstantPolicies.DynamicPermission)]
[DisplayName("کنترلر نمونه با سطح دسترسی پویا")]
public class DynamicPermissionsSampleController : Controller
به صورت خودکار توسط DynamicPermissionsAuthorizationHandler مدیریت می‌شود.


سرویس ISecurityTrimmingService چگونه کار می‌کند؟

کدهای کامل ISecurityTrimmingService را در کلاس SecurityTrimmingService می‌توانید مشاهده کنید.
پیشنیاز درک عملکرد آن، آشنایی با دو قابلیت زیر هستند:
الف) «روش یافتن لیست تمام کنترلرها و اکشن متدهای یک برنامه‌ی ASP.NET Core»
دقیقا از همین سرویس توسعه داده شده‌ی در مطلب فوق، در اینجا نیز استفاده شده‌است؛ با یک تفاوت تکمیلی:
public interface IMvcActionsDiscoveryService
{
    ICollection<MvcControllerViewModel> MvcControllers { get; }
    ICollection<MvcControllerViewModel> GetAllSecuredControllerActionsWithPolicy(string policyName);
}
از متد GetAllSecuredControllerActionsWithPolicy جهت یافتن تمام اکشن متدهایی که مزین به ویژگی Authorize هستند و دارای Policy مساوی DynamicPermission می‌باشند، در کنترلر DynamicRoleClaimsManagerController برای لیست کردن آن‌ها استفاده می‌شود. اگر این اکشن متد مزین به ویژگی DisplayName نیز بود (مانند مثال فوق و یا کنترلر نمونه DynamicPermissionsSampleController)، از مقدار آن برای نمایش نام این اکشن متد استفاده خواهد شد.
بنابراین همینقدر که تعریف ذیل یافت شود، این اکشن متد نیز در صفحه‌ی مدیریت سطوح دسترسی پویا لیست خواهد شد.
 [Authorize(Policy = ConstantPolicies.DynamicPermission)]

ابتدا به مدیریت نقش‌های ثابت سیستم می‌رسیم. سپس به هر نقش می‌توان یک ‍Claim جدید را با مقدار area:controller:action انتساب داد.
به این ترتیب می‌توان به یک نقش، تعدادی اکشن متد را نسبت داد و سطوح دسترسی به آن‌ها را پویا کرد. اما ذخیره سازی آن‌ها چگونه است و چگونه می‌توان به اطلاعات نهایی ذخیره شده دسترسی پیدا کرد؟


مفهوم جدید Role Claims در ASP.NET Core Identity

تا اینجا موفق شدیم تمام اکشن متدهای دارای سیاست دسترسی سفارشی سازی شده‌ی خود را لیست کنیم، تا بتوان آن‌ها را به صورت دلخواهی انتخاب کرد و سطوح دسترسی به آن‌ها را به صورت پویا تغییر داد. اما این اکشن متدهای انتخاب شده را در کجا و به چه صورتی ذخیره کنیم؟
برای ذخیره سازی این اطلاعات نیازی نیست تا جدول جدیدی را به سیستم اضافه کنیم. جدول جدید AppRoleClaims به همین منظور تدارک دیده شده‌است.



وقتی کاربری عضو یک نقش است، به صورت خودکار Role Claims آن نقش را نیز به ارث می‌برد. هدف از نقش‌ها، گروه بندی کاربران است. توسط Role Claims می‌توان مشخص کرد این نقش‌ها چه کارهایی را می‌توانند انجام دهند. اگر از قسمت قبل بخاطر داشته باشید، سرویس توکار UserClaimsPrincipalFactory دارای مرحله‌ی 5 ذیل است:
«5) اگر یک نقش منتسب به کاربر دارای Role Claim باشد، این موارد نیز واکشی شده و به کوکی او به عنوان یک Claim جدید اضافه می‌شوند. در ASP.NET Identity Core نقش‌ها نیز می‌توانند Claim داشته باشند (امکان پیاده سازی سطوح دسترسی پویا).»
به این معنا که با لاگین شخص به سیستم، تمام اطلاعات مرتبط به او که در جدول AppRoleClaims وجود دارند، به کوکی او به صورت خودکار اضافه خواهند شد و دسترسی به آن‌ها فوق العاده سریع است.

در کنترلر DynamicRoleClaimsManagerController، یک Role Claim Type جدید به نام DynamicPermissionClaimType اضافه شده‌است و سپس ID اکشن متدهای انتخابی را به نقش جاری، تحت Claim Type عنوان شده، اضافه می‌کند (تصویر فوق). این ID به صورت area:controller:action طراحی شده‌است. به همین جهت است که در  DynamicPermissionsAuthorizationHandler همین سه جزء از سیستم مسیریابی استخراج و در سرویس SecurityTrimmingService مورد بررسی قرار می‌گیرد:
 return user.HasClaim(claim => claim.Type == ConstantPolicies.DynamicPermissionClaimType &&
claim.Value == currentClaimValue);
در اینجا user همان کاربرجاری سیستم است. HasClaim جزو متدهای استاندارد آن است و Type انتخابی، همان نوع سفارشی مدنظر ما است. currentClaimValue دقیقا همان ID اکشن متد جاری است که توسط کنار هم قرار دادن area:controller:action تشکیل شده‌است.
متد HasClaim هیچگونه رفت و برگشتی را به بانک اطلاعاتی ندارد و اطلاعات خود را از کوکی شخص دریافت می‌کند. متد user.IsInRole نیز به همین نحو عمل می‌کند.


Tag Helper جدید SecurityTrimming

اکنون که سرویس ISecurityTrimmingService را پیاده سازی کرده‌ایم، از آن می‌توان جهت توسعه‌ی SecurityTrimmingTagHelper نیز استفاده کرد:
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            context.CheckArgumentIsNull(nameof(context));
            output.CheckArgumentIsNull(nameof(output));

            // don't render the <security-trimming> tag.
            output.TagName = null;

            if(_securityTrimmingService.CanCurrentUserAccess(Area, Controller, Action))
            {
                // fine, do nothing.
                return;
            }

            // else, suppress the output and generate nothing.
            output.SuppressOutput();
        }
عملکرد آن نیز بسیار ساده است. اگر کاربر، به area:controller:action جاری دسترسی داشت، این Tag Helper کاری را انجام نمی‌دهد. اگر خیر، متد SuppressOutput را فراخوانی می‌کند. این متد سبب خواهد شد، هر آنچه که داخل تگ‌های این TagHelper قرار گرفته، در صفحه رندر نشوند و از خروجی آن حذف شوند. به این ترتیب، کاربر به اطلاعاتی که به آن دسترسی ندارد (مانند لینک به مدخلی خاص را) مشاهده نخواهد کرد. به این مفهوم security trimming می‌گویند.
نمونه‌ای از کاربرد آن‌را در ReportsMenu.cshtml_ می‌توانید مشاهده کنید:
            <security-trimming asp-area="" asp-controller="DynamicPermissionsTest" asp-action="Products">
                <li>
                    <a asp-controller="DynamicPermissionsTest" asp-action="Products" asp-area="">
                        <span class="left5 fa fa-user" aria-hidden="true"></span>
                        گزارش از لیست محصولات
                    </a>
                </li>
            </security-trimming>
در اینجا اگر کاربر جاری به کنترلر DynamicPermissionsTest و اکشن متد Products آن دسترسی پویا نداشته باشد، محتوای قرارگرفته‌ی داخل تگ security-trimming را مشاهده نخواهد کرد.

برای آزمایش آن یک کاربر جدید را به سیستم DNT Identity اضافه کنید. سپس آن‌را در گروه نقشی مشخص قرار دهید (منوی مدیریتی،‌گزینه‌ی مدیریت نقش‌های سیستم). سپس به این گروه دسترسی به تعدادی از آیتم‌های پویا را بدهید (گزینه‌ی مشاهده و تغییر لیست دسترسی‌های پویا). سپس با این اکانت جدید به سیستم وارد شده و بررسی کنید که چه تعدادی از آیتم‌های منوی «گزارشات نمونه» را می‌توانید مشاهده کنید (تامین شده‌ی توسط ReportsMenu.cshtml_).


مدیریت اندازه‌ی حجم کوکی‌های ASP.NET Core Identity

همانطور که ملاحظه کردید، جهت بالابردن سرعت دسترسی به اطلاعات User Claims و Role Claims، تمام اطلاعات مرتبط با آن‌ها، به کوکی کاربر وارد شده‌ی به سیستم، اضافه می‌شوند. همین مساله در یک سیستم بزرگ با تعداد صفحات بالا، سبب خواهد شد تا حجم کوکی کاربر از 5 کیلوبایت بیشتر شده و توسط مرورگرها مورد قبول واقع نشوند و عملا سیستم از کار خواهد افتاد.
برای مدیریت یک چنین مساله‌ای، امکان ذخیره سازی کوکی‌های شخص در داخل بانک اطلاعاتی نیز پیش بینی شده‌است. زیر ساخت آن‌را در مطلب «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» پیشتر در این سایت مطالعه کردید و در پروژه‌ی DNT Identity بکارگرفته شده‌است.
اگر به کلاس IdentityServicesRegistry مراجعه کنید، یک چنین تنظیمی در آن قابل مشاهده است:
 var ticketStore = provider.GetService<ITicketStore>();
identityOptionsCookies.ApplicationCookie.SessionStore = ticketStore; // To manage large identity cookies
در ASP.NET Identity Core، امکان تدارک SessionStore سفارشی برای کوکی‌ها نیز وجود دارد. این SessionStore  باید با پیاده سازی اینترفیس ITicketStore تامین شود. دو نمونه پیاده سازی ITicketStore را در لایه سرویس‌های پروژه می‌توانید مشاهده کنید:
الف) DistributedCacheTicketStore
ب) MemoryCacheTicketStore

اولی از همان زیرساخت «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» استفاده می‌کند و دومی از IMemoryCache توکار ASP.NET Core برای پیاده سازی مکان ذخیره سازی محتوای کوکی‌های سیستم، بهره خواهد برد.
باید دقت داشت که اگر حالت دوم را انتخاب کنید، با شروع مجدد برنامه، تمام اطلاعات کوکی‌های کاربران نیز حذف خواهند شد. بنابراین استفاده‌ی از حالت ذخیره سازی آن‌ها در بانک اطلاعاتی منطقی‌تر است.


نحوه‌ی تنظیم سرویس ITicketStore را نیز در متد setTicketStore می‌توانید مشاهده کنید و در آن، در صورت انتخاب حالت بانک اطلاعاتی، ابتدا تنظیمات کش توزیع شده، صورت گرفته و سپس کلاس DistributedCacheTicketStore به عنوان تامین کننده‌ی ITicketStore به سیستم تزریق وابستگی‌ها معرفی می‌شود.
همین اندازه برای انتقال محتوای کوکی‌های کاربران به سرور کافی است و از این پس تنها اطلاعاتی که به سمت کلاینت ارسال می‌شود، ID رمزنگاری شده‌ی این کوکی است، جهت بازیابی آن از بانک اطلاعاتی و استفاده‌ی خودکار از آن در برنامه.


کدهای کامل این سری را در مخزن کد DNT Identity می‌توانید ملاحظه کنید.
  • #
    ‫۷ سال و ۷ ماه قبل، دوشنبه ۲ اسفند ۱۳۹۵، ساعت ۰۳:۰۵
    با سپاس. اگر بخواهیم در متد HandleRequirementAsync  علاوه بر کاربر جاری و نام کنترلر و نام اکشن، مقدار پارامترهای ارسال شده از کلاینت به اکشن را دریافت کنیم تا بتوانیم بر اساس مقدار آن تصمیم بگیریم، چه باید کرد؟
    • #
      ‫۷ سال و ۷ ماه قبل، دوشنبه ۲ اسفند ۱۳۹۵، ساعت ۰۴:۰۳
      Form Collection دریافتی را بررسی کنید:

      if (mvcContext.HttpContext.Request.Method.Equals("post", StringComparison.OrdinalIgnoreCase))
      {
          foreach (var item in mvcContext.HttpContext.Request.Form)
          {
              var formField = item.Key;
              var formFieldValue = item.Value;
          }
      }
      • #
        ‫۷ سال و ۷ ماه قبل، چهارشنبه ۴ اسفند ۱۳۹۵، ساعت ۰۰:۱۲
        با سلام. اگر از سمت کلاینت dataType از نوع json را به اکشن ارسال کنیم و در پارامترهای اکشن از [FromBody] استفاده کنیم، خاصیت Request.Form برای دریافت پارامترها چه کار باید کرد؟ به نظر می‌رسد خاصیت Request.Form جوابگو نیست.
        • #
          ‫۷ سال و ۷ ماه قبل، چهارشنبه ۴ اسفند ۱۳۹۵، ساعت ۰۴:۵۳
          برای اینکار باید Request.Body را خواند و پردازش کرد (^ و ^).
    • #
      ‫۱ سال و ۵ ماه قبل، دوشنبه ۸ اسفند ۱۴۰۱، ساعت ۱۳:۳۸
      با سلام. اگر چندین کاربر یک نقش یکسانی داشته باشند و بخواهیم که یکی از کاربران را به یکی از صفحاتی که در این role هست عدم دسترسی بدهیم به صورتی که نخواهیم یک نقش جدید با  policy‌های جدید بدهیم در این روش امکان پذیر هست؟ کارفرما روشی را می‌خواهد که علاوه بر این که بتواند نقش‌های مختلفی بر اساس policy  تعریف کند یک روشی هم باشد که یک کاربر خاصی را بتواند عدم دسترسی به یک صفحه خاص را بدهد هر چند که قبلا به آن  کاربر نقشی را داده باشید که دسترسی به آن صفحه را دارد.
      با تشکر
      • #
        ‫۱ سال و ۵ ماه قبل، دوشنبه ۸ اسفند ۱۴۰۱، ساعت ۱۴:۳۱
        مدیریت کننده‌ی سیاست دسترسی پویای نمونه‌ی DynamicPermissionsAuthorizationHandler ، بر اساس نتیجه‌ی تصمیم گیری در متد CanUserAccess کار می‌کند. کارکرد این متد را به دلخواه خودتان تغییر دهید. در حال حاضر این متد در نهایت بر اساس مقدار claim انتسابی، یعنی ناحیه/کنترلر/اکشن متد جاری:
        var currentClaimValue = $"{area}:{controller}:{action}"
        تصمیم گیری می‌کند. به انتهای آن یک hasAccess هم اضافه کنید. مقدار دهی آن‌را به صفحه‌ی لیست کننده‌ی اکشن متدها اضافه کنید و سپس آ‌ن‌را به صورت سفارشی در اینجا استخراج و پردازش کنید.
  • #
    ‫۷ سال و ۲ ماه قبل، جمعه ۲۳ تیر ۱۳۹۶، ساعت ۱۵:۴۸
    سلام؛ در متد CanUserAccess کلاس SecurityTrimmingService:
     public bool CanUserAccess(ClaimsPrincipal user, string area, string controller, string action)
            {
                var currentClaimValue = $"{area}:{controller}:{action}";
                var securedControllerActions = _mvcActionsDiscoveryService.GetAllSecuredControllerActionsWithPolicy(ConstantPolicies.DynamicPermission);
                if (!securedControllerActions.SelectMany(x => x.MvcActions).Any(x => x.ActionId == currentClaimValue))
                {
                    throw new KeyNotFoundException($@"The `secured` area={area}/controller={controller}/action={action} with `ConstantPolicies.DynamicPermission` policy not found. Please check you have entered the area/controller/action names correctly and also it's decorated with the correct security policy.");
                }
    
                if (!user.Identity.IsAuthenticated)
                {
                    return false;
                }
    
                if (user.IsInRole(ConstantRoles.Admin))
                {
                    // Admin users have access to all of the pages.
                    return true;
                }
    
                // Check for dynamic permissions
                // A user gets its permissions claims from the `ApplicationClaimsPrincipalFactory` class automatically and it includes the role claims too.
                return user.HasClaim(claim => claim.Type == ConstantPolicies.DynamicPermissionClaimType &&
                                              claim.Value == currentClaimValue);
            }
    ابتدا بررسی میشه که کاربر دارای دسترسی مورد نظر می‌باشد و سپس چک میشه که کاربر احراز هویت شده و دارای دسترسی admin می‌باشد.
    آیا کاربری که دارای دسترسی Admin می‌باشد نیازی به چک کردن  HasClaim دارد؟
    مثلا اگر این متد به این صورت نوشته شود مشکلی پیش میاد؟
    public bool CanUserAccess(ClaimsPrincipal user, string area, string controller, string action)
            {
                if (!user.Identity.IsAuthenticated)
                {
                    return false;
                }
    
                if (user.IsInRole(ConstantRoles.Admin))
                {
                    // Admin users have access to all of the pages.
                    return true;
                }
    
                var currentClaimValue = $"{area}:{controller}:{action}";
                var securedControllerActions = _mvcActionsDiscoveryService.GetAllSecuredControllerActionsWithPolicy(ConstantPolicies.DynamicPermission);
                if (!securedControllerActions.SelectMany(x => x.MvcActions).Any(x => x.ActionId == currentClaimValue))
                {
                    throw new KeyNotFoundException($@"The `secured` area={area}/controller={controller}/action={action} with `ConstantPolicies.DynamicPermission` policy not found. Please check you have entered the area/controller/action names correctly and also it's decorated with the correct security policy.");
                }
    
                // Check for dynamic permissions
                // A user gets its permissions claims from the `ApplicationClaimsPrincipalFactory` class automatically and it includes the role claims too.
                return user.HasClaim(claim => claim.Type == ConstantPolicies.DynamicPermissionClaimType &&
                                              claim.Value == currentClaimValue);
            }
    • #
      ‫۷ سال و ۲ ماه قبل، جمعه ۲۳ تیر ۱۳۹۶، ساعت ۱۵:۵۴
      «... مشکلی پیش میاد ...»
      خیر. فقط با توجه به اینکه عموما برنامه را با دسترسی ادمین بررسی و اجرا می‌کنید، استثنای احتمالی ابتدای متد را که بیانگر وجود مشکلی است، دریافت نخواهید کرد.
  • #
    ‫۷ سال و ۲ ماه قبل، یکشنبه ۲۵ تیر ۱۳۹۶، ساعت ۱۵:۴۹
    در این روش باید به ازای تمامی اکشن متدها یک کنترلر دسترسی تعریف گردد.
    حالتی رو در نظر بگیرید که دسترسی ویرایش اطلاعات داده شده و داخل فرم ویرایش اطلاعات اکشن متدی هست که به صورت ajax دراپ دانی رو لود میکند.
    اگر برای این اکشن متد دسترسی تعریف نشود خطای 401 صادر میشود.
    چطور میشه این مورد رو هندل کرد که با اعطای دسترسی ویرایش، اکشن مورد استفاده در فرم ویرایش کنترلر جاری (SampleController:GetDropdown:) به Claim اضافه شوند بدون اینکه از ذکر صریح این نوع دسترسی‌ها در فرم "تنظیم سطوح دسترسی پویای نقش ها" خودداری کرد؟
    • #
      ‫۷ سال و ۲ ماه قبل، یکشنبه ۲۵ تیر ۱۳۹۶، ساعت ۱۶:۲۹
      چنین مثالی هم اکنون موجود است:
      - درخواست Ajax ایی
      - اکشن متد متناظر با آن
      علت اینکه نیازی به تعریف مجدد دسترسی در اینجا نبوده، مرتبط است به بحث «نحوه‌ی تعریف میدان دید فیلترها» یا «... ب) اعمال شده‌ی به یک کنترلر که به تمام اکشن متدهای آن کنترلر اعمال خواهد شد... » 
      • #
        ‫۷ سال و ۲ ماه قبل، یکشنبه ۲۵ تیر ۱۳۹۶، ساعت ۱۷:۱۶
        ممنون بابت پاسخگویی.
        فرض کنید در SampleController، اکشنی به نام Edit وجود دارد که سبب نمایش فرم ویرایش میشود که این فرم حاوی دراپ دانی می‌باشد که  اطلاعات خود رو از اکشنی مجزا به نام GetData در همان کنترلر به صورت ajax دریافت میکند.اگر بخواهیم با روش فعلی به این موارد دسترسی دهیم به حالت زیر خواهیم رسید:
         مشاهده فرم ویرایش   SampleController:Edit  ✓ 
         مشاهده محتویات دراپ دان  SampleController:GetData  ✓ 
        در این حالت صفحه بدون مشکل لود شده و محتویات دراپ دان نمایش داده میشود.حالتی که تنها اکشن  index به calim افزوده شود در این حالت صفحه بارگذاری میشود اما محتویات دراپ دان به دلیل اینکه در Calim وجود ندارد لود نخواهد شد و درخواست ajax با خطای 401 مواجه میشود. 
        روشی وجود دارد که با انتخاب اکشن Edit تمامی اکشن‌های مورد نیاز در فرم ویرایش نیز به Calim اضافه گردد؟
        • #
          ‫۷ سال و ۲ ماه قبل، یکشنبه ۲۵ تیر ۱۳۹۶، ساعت ۱۷:۴۱
          « ... تمامی اکشن‌های مورد نیاز  ... »
          این مورد نیازها را در لیست صفحه‌ی دادن دسترسی‌های پویا انتخاب کنید؛ چون به علت «مبهم بودن» قابل حدس زدن یا استخراج نیستند. ضمن اینکه هیچ الزامی وجود ندارد که یک شخص به تمام آدرس‌های قید شده‌ی در یک صفحه دسترسی داشته باشد و برنامه به صورت خودکار به تمام آن‌ها دسترسی کاملی را اعطا کند. هر نوع حدس زدنی در اینجا می‌تواند تبدیل به یک باگ امنیتی در دراز مدت شود.
      • #
        ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۱۷:۴۳
        در مثالی که شما لینک دادید من متوجه اختلاف و دلیل نشدم.
        می خوام از یک گرید Telerik استفاده کنم، در UserManagerController   یک متد اضافه می‌کنم به شکل زیر 
                public IActionResult Users_Read([DataSourceRequest] DataSourceRequest request)
                {
                    return Json(_userManager.Users.ToDataSourceResult(request));
                }

        کد گرید هم به این صورت هست
        @(Html.Kendo().Grid<User>()
                    .Name("grid")
                    .Columns(columns =>
                    {
                        columns.Bound(c => c.FirstName).Width(140);
                        columns.Bound(c => c.LastName).Width(140);
                        columns.Bound(c => c.UserName).Width(140);
                    })
                    .HtmlAttributes(new { style = "height: 380px;" })
                    .Pageable(pageable => pageable
                        .Refresh(true)
                        .PageSizes(true)
                        .ButtonCount(5))
                    .DataSource(dataSource => dataSource
                        .Ajax()
                        .Read(read => read.Action("Users_Read", "UserManager"))
                    )
        )
        ولی باز هم خطای 401 دریافت می‌کنم.

        اگر ممکنه باشه می‌خوام شبیه مثال خودتون، برای این متد دستری اعمال نکنم. آیا این امکان دارد؟ یا حتما باید شبیه Action‌های دیگر دسترسی پویا براش در نظر بگیرم؟

        • #
          ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۱۹:۲۷
          خطای 410 چیزی شبیه به 404 یا یافت نشد است. علت آن هم عدم ذکر area جاری است (و همچنین UsersManager است؛ یک s بعد از User هست):
            .Read(read => read.Action("Users_Read", "UsersManager", new { area = AreaConstants.IdentityArea }))
          • #
            ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۱۹:۳۸
            بله، حق با شماست. با اضافه کردن area  درست شد. باید به لینک درخواست (که اشتباه است = UsersManager/Users_Read) دقت می‌کردم.
            درخواست باید به صورت identity/UsersManager/Users_Read باشد.
  • #
    ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۱۳:۱۷
    آیا میشود در کنار سیستم احراز هویت DNT Identity سیستم احراز هویت Active Directory را پیاده سازی کردی؟
    به اینصورت که در Request کاربر چک شود که اگر از کاربران جوین دامین است نیاز به لاگین نباشد و ساخت کوکی و لاگین پشت سیستم انجام شود و فقط دسترسی آن چک شود. درغیر اینصورت کاربر به صفحه لاگین راهنمایی شود. نیاز دارم که هر دو سیستم احراز هویت را داشته باشم...برای کاربران داخل و خارج سازمان 
    • #
      ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۱۴:۳۴
      برای کار با اکتیو دایرکتوری کار زیادی لازم نیست انجام شود. مرحله‌ی اول آن فعالسازی Windows Authentication در برنامه‌های ASP.NET Core است. در این حالت همینقدر که کاربر، به ویندوز متصل به شبکه و اکتیو دایرکتوری وارد شده باشد، به سیستم لاگین خواهد شد. نیازی به تغییری هم در هیچ قسمتی از برنامه نیست. مرحله‌ی بعد آن کار با نقش‌های سیستم است. این نقش‌ها هم در User.Identity هم اکنون موجود هستند و این شیء توسط Windows Authentication و گروه‌های اکتیو دایرکتوری پیش‌تر به صورت خودکار مقدار دهی شده‌است:
      - نحوه‌ی یکپارچه سازی با سیستم اعتبارسنجی ویندوز  (با این فعالسازی، ویژگی‌های [Authorize] و [AllowAnonymous] بلافاصله قابل استفاده خواهند بود و User.Identity هم به صورت خودکار مقداردهی می‌شود.)
      - یک نمونه مثال از دسترسی به نقش‌های اکتیودایرکتوری (با این تنظیمات برای مثال دسترسی به نقش‌های [Authorize(Roles = "Domain Admins")] و [Authorize(Roles = "Domain Users")] را خواهید داشت. یعنی برای مثال هرکاربری که عضو گروه Domain admins در اکتیو دایرکتوری باشد، امکان دسترسی به مورد اول را خواهد یافت.)
      • #
        ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۱۶:۰۴
        با نحوه‌ی یکپارچه سازی با سیستم اعتبارسنجی ویندوز موفق به لاگین با اکتیو دایرکتوری شدم (البته فقط در حالت دیباگ و با dotnet projectName). مشکلی بعد اینجاست که میخوام از دسترسی Dynamic استفاده کنم. زمانیکه کاربر با Windows Authentication لاگین میکنه، دیگه اون دسترسی‌هایی که توی سیستم بهش دادم لود نمیشه. یک یوزر با نام کاربری مورد نظر توی سیستم تعریف کردم و دسترسی بهش دادم. ولی فقط زمانیکه از فرم لاگین وارد سیستم میشه، دسترسی‌هاش لود میشه. خلاصه که Claims کاربر ساخته نمیشه.
        آیا امکانش هست که درصورت ورود با Windows Authentication به دسترسی‌های خودش دسترسی داشته باشه؟
        آیا لازمه بعد از ورود با
        Windows Authentication یوزر مشابهش رو در بانک جستجو کنم و اون رو هم با _signInManager.PasswordSignInAsync  لاگین کنیم تا به اطلاعات دسترسی داشته باشم؟
         

        • #
          ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۱۶:۴۵
          برای افزودن لیست Claims کاربر موجود در بانک اطلاعاتی به لیست Claims کاربر وارد شده‌ی توسط اکتیو دایرکتوری، باید از یک IClaimsTransformation سفارشی استفاده کنید تا این نگاشت را انجام دهد (نمونه‌اش در مطلب « سفارشی سازی ASP.NET Core Identity - قسمت چهارم - User Claims » به نحو دیگری استفاده شده‌است):
          public class ApplicationClaimsTransformation : IClaimsTransformation
          {
          }
          پیاده سازی کامل آن در اینجا
          و برای ثبت آن:
          services.AddScoped<IClaimsTransformation, ApplicationClaimsTransformation>();
          • #
            ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۱۸:۲۶
            کجا باید این متد را به کار برد؟
            TransformAsync(ClaimsPrincipal principal) 
            • #
              ‫۶ سال و ۱۱ ماه قبل، شنبه ۲۹ مهر ۱۳۹۶، ساعت ۲۲:۴۵
              همینقدر که قسمت ثبت آن‌را در سیستم تزریق وابستگی‌های برنامه انجام داده باشید، با فراخوانی httpContext.AuthenticateAsync، متد TransformAsync کلاس ApplicationClaimsTransformation به صورت خودکار فراخوانی و اجرا خواهد شد و در نتیجه‌ی آن، امکان افزودن Claims سفارشی، به Claims موجود میسر می‌شود. چون از Windows Authentication استفاده می‌کنید، با استفاده‌ی از IISMiddleware، کار فراخوانی httpContext.AuthenticateAsync به صورت خودکار انجام می‌شود و در نتیجه‌ی آن، اجرای متد TransformAsync خودکار است.
              • #
                ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۳۰ مهر ۱۳۹۶، ساعت ۰۱:۴۰
                بله درست میفرمایید ولی هنوز هم لیست Claims کاربر موجود در بانک اطلاعاتی به لیست Claims کاربر وارد شده‌ی توسط اکتیو دایرکتوری اضافه نمیشه.
                تنظیمات رو چک میکنم همه چیز درست است ولی...


                • #
                  ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۳۰ مهر ۱۳۹۶، ساعت ۰۱:۵۵
                  IClaimsTransformation مربوط به NET Core 2.x. هست. در نگارش 1x نام دیگری داشته‌است (IClaimsTransformer بوده).
                  • #
                    ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۳۰ مهر ۱۳۹۶، ساعت ۰۲:۵۹
                    بعد از این تغییر نام هنوز هم لیست Claims پر نمیشه...حتی لاگ هم نمیدازه...کلا IClaimsTransformer کال نمیشه
                  • #
                    ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۳۰ مهر ۱۳۹۶، ساعت ۱۷:۴۸
                    آخرین نسخه از DNT Identity رو دانلود کردم و تنظیمات رو اعمال کردم.




                    متد
                    TransformAsync فقط درحالت لاگین بصورت معمولی کال میشه ولی در حالت Windows Authentication کال نمیشه...لاگ هم ثبت نمیکنه
  • #
    ‫۶ سال و ۵ ماه قبل، سه‌شنبه ۲۸ فروردین ۱۳۹۷، ساعت ۲۲:۲۸
    من می‌خواهم این پروژه رو بصورت Db first  استفاده کنم. یک بار پروژه شما رو اجرا کردم بانک ایجاد شده. الان چه تغییراتی نیاز هست تا بتونم بصورت db first  استفاده کنم؟
  • #
    ‫۶ سال و ۴ ماه قبل، سه‌شنبه ۱۸ اردیبهشت ۱۳۹۷، ساعت ۱۵:۵۷
    با سلام؛ من از  tag helper security-trimming برای منوها استفاده کردم؛ تمامی تنظیمات اعم از  ViewImports و همچنین کلاس SecurityTrimmingTagHelper مانند توضیحات بالا. اما وقتی پروژه رو دیباگ میکنم وارد متد Process نمی‌شود. آیا  تنظیمات خاصی رو از قلم انداختم؟
  • #
    ‫۶ سال و ۳ ماه قبل، چهارشنبه ۲۳ خرداد ۱۳۹۷، ساعت ۰۴:۰۰
    سلام؛ اگر بخواهیم از این سیستم در Angular 5 استفاده کنیم و از Asp core به عنوان Back End استفاده کنیم و اطلاعات را با استفاده از Web Api، اطلاعات مربوط به Identity را بفرستیم به انگولار، باید چیکار کنم؟
    • #
      ‫۶ سال و ۳ ماه قبل، چهارشنبه ۲۳ خرداد ۱۳۹۷، ساعت ۰۵:۰۵
      - لیست صفحاتی را که کاربر به آن دسترسی دارد، پس از لاگین برای او ارسال کنید (یا به صورت یک آرایه که در سمت Angular کش شود یا به صورت Claims سفارشی داخل توکن JWT او؛ مانند Claims سفارشی نقش‌ها که هم اکنون موجود است).
      - سپس توسط یک محافظ مسیر سفارشی، دسترسی‌ها را از این آرایه خوانده و اعمال کنید. برای نمونه محافظ مسیر سفارشی یاد شده، بر اساس Claims مربوط به نقش‌های کاربر که پس از لاگین به سمت کلاینت ارسال می‌شوند، دسترسی به مسیرها را کنترل می‌کند.
  • #
    ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۱۹:۴۴
    آیا برای اعمال دسترسی پویا در سطح داده هم ایده ای هست؟
    مثلا در یک جدول، دسترسی افراد با رکورد‌ها تفاوت دارد، یک نفری یک سری از رکوردها را می‌تواند در گرید مشاهده کند، و نفر دیگر (با مشخصات کاربری دیگر) سری دیگر رکورد‌ها را می‌تواند مشاهده کند.
    منظور از سوال این است که لازم نباشد که با if else داده‌ها واکشی شود، و بر اساس نقش/دسترسی فیلتر بر روی داده‌ها اعمال شود.
    • #
      ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۱۹:۵۷
      - آیا در داده‌های شما مشخص است هر رکورد توسط چه شخصی قابل مشاهده است (مثلا به ازای هر رکوردی که ثبت شده، یک فیلد سطوح دسترسی هم هست؛ با رابطه‌ی یک به چند البته)؟ چون عموما کسی چنین کاری را انجام نمی‌دهد (به علت پیچیدگی بیش از حد، نیاز به ثبت اطلاعات بیشتر برای کاربر و اپراتور(؟) و چند سطحی شدن و نیاز به join اضافی و ... آیا باید به اپراتوری که داده‌ها را ثبت می‌کند در این حالت اطمینان کرد که خودش را داخل لیست قرار ندهد؟). روش متداول این است که اگر شخصی به صفحه‌ای دسترسی داشت، یعنی به تمام اجزا و اطلاعات آن دسترسی دارد. یکسری از گزارش‌ها مدیریتی هستند و یکسری عمومی. اگر گزارشی مدیریتی است، نصف آن برای کاربر معمولی نمایش داده نمی‌شود. کل آن برای کاربر معمولی قابل دسترسی نیست. 
      - گیرم چنین فیلد اضافی را هم به هر رکورد اضافه و ثبت کردید، روش اعمال خودکار آن همانند بحث‌های soft delete است که در EF Core به صورت توکار پشتیبانی می‌شود.
      - همچنین در مورد دیدن یا ندیدن قسمت‌هایی از صفحه که شخص بر اساس نقش‌های او نباید آن‌ها را مشاهده کند، یک tag helper ویژه به نام «SecurityTrimmingTagHelper» طراحی شده‌است که توضیحات آن در متن جاری هست.
      • #
        ‫۶ سال و ۲ ماه قبل، جمعه ۸ تیر ۱۳۹۷، ساعت ۲۰:۱۰
        مواردی که اشاره کردید کاملا صحیح است.
        موردی را در نظر بگیرید که کاربران به صورت سلسله مراتبی تعریف می‌شوند (تعریف کاربر و محل قرارگیری کاربر در درخت کاربران پویا است)
        ما صفحه ای داریم که در آن لاگ رفتار کاربران در آن مشاهده می‌شود، با این قید که هر کاربر می‌تواند فقط لاگ کاربران زیر مجوعه خود را مشاهده کند (یعنی فیلد‌های سطوح دسترسی هست و ما می‌توانیم رکورد‌های مورد نظر را استخراج کنیم) ما به کاربران اجازه مشاهده صفحه (مشاهده لاگ) را می‌دهیم، طبیعتا چون کاربران پویا هستند امکان ایجاد صفحه به ازای هر کاربر وجود ندارد. در این شرایط ایده ای غیر از اعمال دستی فیلتر‌های برای واکشی، هست؟
  • #
    ‫۶ سال قبل، چهارشنبه ۲۱ شهریور ۱۳۹۷، ساعت ۲۰:۰۷
    با سلام. بنده از این سیستم دارم استفاده می‌کنم. اما نیاز به این دارم که به عنوان مثال یک دکمه در صفحه نمایش داده نشود و یا اینکه شیوه نمایش برای یک سطح دسترسی متفاوت باشد. در مجموع نیاز دارم تا این سطح دسترسی در سطح View اعمال بشه. چطور می‌تونم این کار رو انجام بدم. آیا ایجاد جدول Claim منطقی هست؟؟
    باتشکر.
    • #
      ‫۶ سال قبل، چهارشنبه ۲۱ شهریور ۱۳۹۷، ساعت ۲۲:۵۹
      همان قسمت «Tag Helper جدید SecurityTrimming» مطلب جاری است.
  • #
    ‫۵ سال و ۸ ماه قبل، چهارشنبه ۲۶ دی ۱۳۹۷، ساعت ۱۳:۰۰
    با سلام؛ برای فرم‌های که بصورت  modal dialog view وقتی که  unauthorized می‌شود به صفحه  لاگین هدایت نمی‌شود. چه راه حلی پیشنهاد می‌کنید.

      • #
        ‫۵ سال و ۸ ماه قبل، چهارشنبه ۲۶ دی ۱۳۹۷، ساعت ۱۸:۵۷
        من از دیالوگ استفاده میکنم که موقع باز کردن صفحات بصورت آژاکسی نیست. یک url رو باز می‌کند چون به اون Url دسترسی ندارد صفحه هدایت به لاگین نمی‌شود.
        $("#dialog").dialog({ autoOpen: false, open: function (event, ui) {   $(this).load(url);  }) });
        <div id="dialog" title="Basic dialog">
            <p>Dialog box</p>
        </div>
        • #
          ‫۵ سال و ۸ ماه قبل، چهارشنبه ۲۶ دی ۱۳۹۷، ساعت ۲۱:۱۵
          complete: function (xhr, status) {
            if (xhr.status === 403 || xhr.status === 401) {
          در مورد متد load ای هم که ارسال کردید چنین چیزی وجود دارد:
          $("#success").load(url, function( response, status, xhr ) {
            if (xhr.status === 403 || xhr.status === 401) {
                window.location = xhr.getResponseHeader('Location');
            }
          });
  • #
    ‫۵ سال و ۶ ماه قبل، جمعه ۱۰ اسفند ۱۳۹۷، ساعت ۰۱:۵۰
    با سلام؛ میخواستم این مبحث DynamicPermissionPolicy رو با حداقل‌ترین وابستگی‌ها روی یک پروژه که از CookieAuthentication بدون ASP.NET Identity است استفاده کنم، مباحث افزودن سرویس‌ها و TagHelper‌ها را بخوبی متوجه شدم، در ادامه برای ایجاد یک جدول UserClaim سفارشی و افزودن آن به IdentityClaim که بطور خودکار Permission‌های داینامیک رو شناسایی کنه چه کاری باید انجام داد؟
      • #
        ‫۵ سال و ۶ ماه قبل، جمعه ۱۰ اسفند ۱۳۹۷، ساعت ۰۲:۱۵
        با تشکر. متوجه این مورد شدم، منظورم بیشتر ساختار Entity موردنیاز در بانک اطلاعاتی است چون در CookieAuthentication جداول بصورت دلخواه ساخته میشوند. اگر به این شکل ساخته شود و داده‌ها هم به این شکل وارد شوند صحیح است؟

        و سوال دیگری که ذهنم را درگیر کرده بود، آیا در متد CreateCookieClaimsAsync ، مقادیر ClaimTypeها نیز باید مانند Role‌های کاربر در یک حلقه وارد سیستم ClaimsIdentity شوند؟ 
        • #
          ‫۵ سال و ۶ ماه قبل، جمعه ۱۰ اسفند ۱۳۹۷، ساعت ۰۲:۵۲
          در ساختار درونی سیستم ASP.NET Core Identity، در عمل چیزی به نام Role وجود خارجی ندارد. این Roleهای ظاهری فقط برای سازگاری با سیستم‌های membership خیلی قدیمی وجود دارند. تمام سیستم Identity بر اساس User Claims کار می‌کند. تمام Roleها و غیره در پشت صحنه ابتدا تبدیل به user claims می‌شوند و سپس استفاده خواهند شد. اطلاعات بیشتر: «ASP.NET Core Identity چگونه اطلاعات جدول AppUserClaims را پردازش می‌کند؟»
  • #
    ‫۵ سال و ۶ ماه قبل، جمعه ۱۰ اسفند ۱۳۹۷، ساعت ۰۳:۵۱
    با سلام؛ من در اجرای پروژه دچار خطای
    InvalidOperationException: Unable to resolve service for type 'DNTCommon.Web.Core.IMvcActionsDiscoveryService' while attempting to activate '[ProjectName].Services.SecurityTrimmingService'.
    میشوم. علت چیست؟
  • #
    ‫۵ سال و ۶ ماه قبل، یکشنبه ۱۹ اسفند ۱۳۹۷، ساعت ۱۲:۰۹
    با سلام و سپاس؛
    در روش ذخیره سازی کوکی‌ها در دیتابیس، آیا تمام Role Claims ی کاربر(که بعضا بیش از صد claim میشود) در همان یک رکورد جدول کش ذخیره میشود؟
    • #
      ‫۵ سال و ۶ ماه قبل، یکشنبه ۱۹ اسفند ۱۳۹۷، ساعت ۱۲:۴۰
      بله. البته باز هم یک کوکی بسیار کوچک و چند بایتی، حاوی مشخصات ID رمزنگاری شده‌ی آن تولید می‌شود که هدف از آن، بازیابی اطلاعات اصلی این کوکی از بانک اطلاعاتی و استفاده‌ی خودکار از آن در برنامه است .
  • #
    ‫۵ سال و ۶ ماه قبل، سه‌شنبه ۲۸ اسفند ۱۳۹۷، ساعت ۲۲:۲۰
    این بخش برای چه منظوری استفاده شده؟ در متد HandleRequirementAsync 
                var request = mvcContext.HttpContext.Request;
                if (request.Method.Equals("post", StringComparison.OrdinalIgnoreCase))
                {
                    if (request.IsAjaxRequest() && request.ContentType.Contains("application/json"))
                    {
                        var httpRequestInfoService = mvcContext.HttpContext.RequestServices.GetService<IHttpRequestInfoService>();
                        var model = await httpRequestInfoService.DeserializeRequestJsonBodyAsAsync<RoleViewModel>();
                        if (model != null)
                        {
    
                        }
                    }
                    else
                    {
                        foreach (var item in request.Form)
                        {
                            var formField = item.Key;
                            var formFieldValue = item.Value;
                        }
                    }
                }

    • #
      ‫۵ سال و ۶ ماه قبل، سه‌شنبه ۲۸ اسفند ۱۳۹۷، ساعت ۲۲:۵۱
      مثال هست جهت نشان دادن روش دسترسی به اطلاعات ارسالی توسط کاربر در دو حالت Ajax ای و معمولی، در این قسمت ویژه.
  • #
    ‫۵ سال و ۵ ماه قبل، یکشنبه ۴ فروردین ۱۳۹۸، ساعت ۱۷:۴۳
    سلام؛ در یک پروژه خاصی نقش کاربران وابسته به منطقه ای که انتخاب می‌کنند تغییر می‌کنه. برای مثال فرد الف در صورت لاگین و انتخاب شهر تهران نقش سرپرست دارد ولی در صورتی که شهر شیراز را انتخاب کند نقش بازرسی دارد. در این مثال از فرد الف بعد از لاگین پرسیده می‌شود که بین شهر تهران و شیراز انتخاب کند تا متناسب آن نقش آن تعیین گردد. همچنین در هر لحظه این امکان برای او وجود دارد با تغییر شهر به سطوح تعیین شده برای آن شهر دسترسی پیدا کند. برای پیاده سازی این کار توسعه کلاس UserRole کارایی لازم را ندارد چون به صورت پیشرفض کلید این جدول روی یوزر و نقش تنظیم شده است. در حالی که برای پیاده سازی این قسمت نیاز به یک کلید سه تایی از یوزر،نقش و منطقه می‌باشد. برای توسعه چنین ساختاری چه پیشنهادی می‌کنید؟
    • #
      ‫۵ سال و ۵ ماه قبل، یکشنبه ۴ فروردین ۱۳۹۸، ساعت ۱۸:۳۵
      در ساختار درونی سیستم ASP.NET Core Identity، در عمل چیزی به نام Role وجود خارجی ندارد. برای مثال Roleها هم در این سیستم یک User-Claim از نوع ClaimTypes.Role هستند. تمام سیستم Identity بر اساس User Claims کار می‌کند. تمام Roleها و غیره در پشت صحنه ابتدا تبدیل به user claims می‌شوند و سپس استفاده خواهند شد.
      var roles = ((ClaimsIdentity)User.Identity).Claims
                      .Where(c => c.Type == ClaimTypes.Role)
                      .Select(c => c.Value);
      بنابراین برای سفارشی سازی‌های ویژه، نیازی به دستکاری در ساختار جداول پیش‌فرض آن نیست. برای اینکار از جدول ویژه‌ی AppUserClaims و برای طراحی Policy ویژه‌ی پردازش آن، از AuthorizationHandler توضیح داده شده‌ی در مطلب جاری ایده بگیرید.

  • #
    ‫۵ سال و ۴ ماه قبل، شنبه ۳۱ فروردین ۱۳۹۸، ساعت ۱۶:۱۳

    سلام
    زمانی که کاربر رو  با Role ثبت میکنم اجرای پروژه با خطای زیر مواجه میشه(یعنی زمانی که تنظیمات identity رو در قسمت StartUp انجام میدم با این خطا مواجه میشوم.)

    ولی اگه Role ثبت نکنم کاربر ثبت میشه.



  • #
    ‫۵ سال و ۳ ماه قبل، سه‌شنبه ۳۱ اردیبهشت ۱۳۹۸، ساعت ۱۷:۵۷
    آیا با ازدیاد تعداد اکشن‌ها و کنترلرها برای ذخیره سازی Claim ها در یک پروژه بزرگ، مشکل حجیم شدن کوکی یا توکن را نخواهیم داشت؟
    • #
      ‫۵ سال و ۳ ماه قبل، سه‌شنبه ۳۱ اردیبهشت ۱۳۹۸، ساعت ۱۷:۵۹
      مراجعه کنید به قسمت «مدیریت اندازه‌ی حجم کوکی‌های ASP.NET Core Identity» انتهای بحث.
  • #
    ‫۵ سال و ۳ ماه قبل، چهارشنبه ۱ خرداد ۱۳۹۸، ساعت ۱۹:۵۰
    پروژه ای داریم که 3 پروژه webApllication دارد
    adminapplication
    sellerapplication
    webapplication
    حال فقط کاربری که نقش seller دارد یا اینکه دسترسی‌های داینامیک به پروژه seller دارد بتواند لاگین کند
    این بخش رو به چه صورت می‌توان انجام داد؟
    الان بخش ادمین فقط ادمین و دسترسی داینامیک دسترسی دارد. (ادمین میتواند به همه پروژه‌ها دسترسی داشته باشد)
    آیا باید Handler جدیدی تعریف کرد؟
    • #
      ‫۵ سال و ۳ ماه قبل، پنجشنبه ۲ خرداد ۱۳۹۸، ساعت ۰۰:۱۹
      ASP.NET Core Identity فقط برای مدیریت یک برنامه‌ی وب طراحی شده. اگر بیشتر از یک برنامه‌ی وب دارید، نیاز به راه حل مخصوص آن‌را که «تامین کننده‌ی هویت مرکزی» یا «IDP» نام دارد، خواهید داشت: «امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x»  
        • #
          ‫۵ سال و ۳ ماه قبل، جمعه ۳ خرداد ۱۳۹۸، ساعت ۱۵:۵۵
          ربطی به هم ندارند. دو فناوری مستقل هستند. هرچند می‌توان در IDP از Identity core برای مدیریت کاربران هم استفاده کرد. اطلاعات بیشتر
          البته بدون مرور کامل سری «امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x»  قادر به درک مستندات رسمی آن و مطلب فوق نخواهید شد.
          • #
            ‫۵ سال و ۳ ماه قبل، یکشنبه ۵ خرداد ۱۳۹۸، ساعت ۱۰:۳۸
            ممنون بابت ارائه مستندات Identity Server
            مستندات شما به همراه مستندات رسمی مطالعه کردم فقط یک نکته برام ابهام مونده ممنون میشم راهنماییم کنید
            براساس دسترسی‌های داینامیک:
            1-  کاربر 1 به پروژه1 دسترسی و به این کنترلرها و اکشن ها
            2- کاربر 2 به پروژه 2 دسترسی و به این کنترلرها و اکشن ها
            آیا باید جاییکه کلاینت تعریف می‌شود 2 نمونه از کلاینت ساخته شود؟ و اینکه در هر پروژه چگونه باید از این کلاینت‌ها استفاده کرد؟

            • #
              ‫۵ سال و ۳ ماه قبل، یکشنبه ۵ خرداد ۱۳۹۸، ساعت ۱۲:۳۶
              آیا باید جاییکه کلاینت تعریف می‌شود 2 نمونه از کلاینت ساخته شود؟
              بله.
              و اینکه در هر پروژه چگونه باید از این کلاینت‌ها استفاده کرد؟ 
              مفصل در همان سری با ارائه‌ی دو کلاینت مجزای MVC و همچنین Web API برای IDP ارائه شده، بحث شده. در قسمت چهارم آن، بحث scopes معرفی شده. در قسمت پنجم آن، نحوه‌ی دسترسی به این scopes و اینکه چه اطلاعاتی به همراه آن‌ها به کلاینت‌ها منتقل شوند یا خیر. قسمت ششم و هفتم آن، بحث scopes را جامع‌تر معرفی کرده با مثال‌های بیشتری. قسمت هفتم آن کلاینت Web API را هم به همراه تنظیمات خاص آن بررسی کرده و در قسمت هشتم، Authorization Policy‌ها و سیاست‌های دسترسی پیشرفته را بحث کرده.
  • #
    ‫۵ سال و ۳ ماه قبل، سه‌شنبه ۲۱ خرداد ۱۳۹۸، ساعت ۱۴:۱۹
    با سلام؛ بعد از تغییر دسترسی‌های یک نقش، بدون لاگین مجدد، دسترسی‌ها اعمال می‌شود. یعنی به ازای هر درخواست، دسترسی‌ها از دیتابیس واکشی می‌شود؟
    • #
      ‫۵ سال و ۳ ماه قبل، سه‌شنبه ۲۱ خرداد ۱۳۹۸، ساعت ۱۶:۴۴
      خیر. فقط پس از بازسازی کوکی شخص این اتفاق (اعمال واقعی دسترسی‌های شخص) رخ می‌دهد. برای اجبار به بازسازی کوکی شخص، متد Update Security Stamp باید فراخوانی شود.
      • #
        ‫۵ سال قبل، سه‌شنبه ۲۶ شهریور ۱۳۹۸، ساعت ۲۲:۱۷
        من این تابع رو فراخوانی میکنم ولی کاربر همچنان لاگین هست. البته از کش توزیع شده(ذخیره در دیتابیس) که در همین مطلب توضیح داده شد استفاده کردم. بخاطر همین هست یا دلیل دیگه ای داره؟
        • #
          ‫۵ سال قبل، سه‌شنبه ۲۶ شهریور ۱۳۹۸، ساعت ۲۲:۵۱
          دلیل آن عدم استفاده از تنظیمات پیش‌فرض این پروژه مانند زمان بررسی security stamp تغییر کرده‌است. حالت پیش‌فرض آن 30 دقیقه است اما در این پروژه البته به صفر تنظیم شده تا Logout آنی پیاده سازی شود. 30 دقیقه‌ی پیش‌فرض یعنی اگر کاربری را غیرفعال کردید، این شخص تا 30 دقیقه‌ی بعد می‌تواند به خرابکاری خودش ادامه دهد و هنوز لاگین باشد؛ بدون مشکلی!
  • #
    ‫۵ سال و ۱ ماه قبل، جمعه ۲۸ تیر ۱۳۹۸، ساعت ۱۸:۰۴
    در صورتی که بخواهیم از این مطلب استفاده کنیم دیگر نمیتوانیم از اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity خودتان استفاده کنیم ؟ چون در اینجا مجبوریم از identity استفاده کنیم ولی سیستم احراز هویتی که شما ایجاد کرده اید بدون identity است.
  • #
    ‫۵ سال و ۱ ماه قبل، جمعه ۱۸ مرداد ۱۳۹۸، ساعت ۱۶:۱۶
    باسلام ، ممنون بابت توضیحات کاملتون!
    1 - امکانش هست بخش ویو رو با jstree پیاده کنید؟ یا حداقل یه سمپلی مثل این که با jstree پیاده شده باشه رو معرفی کنید.
    البته ناگفته نماند اینجاگفته شده اما روش پیاده سازیش با سمپل شما فرق داره !
    2 - من علاوه بر چهار اکشنی (Crud)که همیشه مرسوم هست و برای اکثر کنترلر‌ها استفاده میشه چند تا اکشن دیگه هم دارم که نمیخوام اسمشون تو لیست دسترسی‌ها نمایان بشه (من از Index برای ورود به ویو و از اکشن List برای نمایش اطلاعات دیتاتیبل بصورت ajax استفاده میکنم) و خب وقتی Policy Attribute رو بالای اکشن‌ها ننویسم نمیاره تا به اینجا درسته اما این زمانی صدق میکنه که بالای کنترلر‌ها از صفت Authorize استفاده نکنم ! اگر استفاده هم بکنم میتونم یه صفتی خودم بنویسم و بگم که اگر این صفات وجود داشت نادیده بگیر حالا بحث اینه چون کنترلر دارای صفت Authorize هست ما اون بخشی که نمایش داده نمیشه رو در دیتابیس بعنوان دسترسی کاربر ثبت نکنیم دیگه سیستم اجازه استفاده از اون اکشن (List)رو نمیده! چطور اینو هندل کنم؟
    3 - بنابر فرمایشات شما اگر مقدار کوکی از 5kb بیشتر بشه مشکلاتی رو بوجود میاره که راه حل هایی رو هم نشون داد.
     - بنظر شما اگر ما از سشن استفاده کنیم بجای کوکی مشکلی داره؟
     - و اگر از دیتابیس استفاده کنیم چطور بجای استفاده از سشن و کوکی که پرمیژن‌ها در اونها قرار میگیره؟ بار اضافی تولید میشه؟
    • #
      ‫۵ سال و ۱ ماه قبل، جمعه ۱۸ مرداد ۱۳۹۸، ساعت ۱۷:۰۲
      - مطلب آن فرد هندی که لینک دادید، به نظر از این مطلب استفاده کرده.
      - برای دسترسی به اکشن متدهای محافظت شده‌ی با ویژگی خالی Authorize (بدون هیچگونه تنظیم دیگری)، تنها وجود شرط ورود به سیستم کافی است. اگر Policy یا Role خاصی بر روی آن‌ها اعمال شود، آنگاه آن شخص باید User Claims ویژه‌ای را داشته باشد تا بتواند شرایط آن Policy را تامین کند. در مورد نوشتن انواع و اقسام Policyهای سفارشی (چه بر اساس شرایط User Claims ثابت (policy => policy.RequireClaim) و چه پویا که به همراه AuthorizationHandlerها هستند) در این مطلب بحث شده و شما محدود به تنظیمات پیش‌فرض پروژه نیستید.
      - مطلب «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» و خصوصا نظرات آن‌را مطالعه کنید.
    • #
      ‫۴ سال و ۸ ماه قبل، جمعه ۲۰ دی ۱۳۹۸، ساعت ۲۲:۵۸
      با سلام؛ در نسخه 3.1 این خطا مشاهده می‌شود:
      InvalidOperationException: Unable to resolve service for type 'ServiceLayer.ISecurityTrimmingService' while attempting to activate 'ServiceLayer.DynamicPermissionsAuthorizationHandler'.
      کد فوق بصورت زیر زده شده:
      public class DynamicPermissionsAuthorizationHandler : AuthorizationHandler<DynamicPermissionRequirement>
          {
              private readonly ISecurityTrimmingService _securityTrimmingService;
      
              public DynamicPermissionsAuthorizationHandler(ISecurityTrimmingService securityTrimmingService)
              {
                  _securityTrimmingService = securityTrimmingService;
                  _securityTrimmingService.CheckArgumentIsNull(nameof(_securityTrimmingService));
              }
      • #
        ‫۴ سال و ۸ ماه قبل، جمعه ۲۰ دی ۱۳۹۸، ساعت ۲۳:۰۲
        مخزن کد این پروژه به ASP.NET Core 3.1 به روز رسانی شده‌است. کدهای خودتان را با آن انطباق دهید.
  • #
    ‫۴ سال و ۷ ماه قبل، یکشنبه ۲۹ دی ۱۳۹۸، ساعت ۱۸:۴۱
    سلام توی پروژه من سطح دسترسی‌ها زیاده وقتی میخوام یه توکن تولید کنم حجم توکن خیلی بالا میره چطور این مشکل را رفع کنم

    اینم کد تولید توکنه

     public async Task<string> GenerateTokenAsync(ApplicationUser User)
            {
                var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
                var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature);
    
                var encrytionKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.EncrypKey);
                var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encrytionKey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
                
    
    
                var tokenDescriptor = new SecurityTokenDescriptor()
                {
                    Issuer = _siteSettings.JwtSettings.Issuer,
                    Audience = _siteSettings.JwtSettings.Audience,
                    IssuedAt = DateTime.Now,
                    NotBefore = DateTime.Now.AddMinutes(_siteSettings.JwtSettings.NotBeforeMinutes),
                    Expires = DateTime.Now.AddMinutes(_siteSettings.JwtSettings.ExpirationMinutes),
                    SigningCredentials = signingCredentials,
                    Subject = new ClaimsIdentity(await GetClaimsAsync(User)),
                    EncryptingCredentials = encryptingCredentials,
                };
    
                var tokenHandler = new JwtSecurityTokenHandler();
                var securityToken = tokenHandler.CreateToken(tokenDescriptor);
                return tokenHandler.WriteToken(securityToken);
            }
    
    
            public async Task<IEnumerable<Claim>> GetClaimsAsync(ApplicationUser User)
            {
                var Claims = new List<Claim>()
                {
                    new Claim(ClaimTypes.Name,User.UserName),
                    new Claim(ClaimTypes.NameIdentifier,User.Id),
                    new Claim(ClaimTypes.MobilePhone,User.PhoneNumber),
                    new Claim(new ClaimsIdentityOptions().SecurityStampClaimType,User.SecurityStamp),
                    new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
                };
                var Roles = _roleManager.Roles.ToList();
                foreach (var item in Roles)
                {
                    var RoleClaims = await _roleManager.FindClaimsInRole(item.Id);
                    foreach (var claim in RoleClaims.Claims)
                    {
                        Claims.Add(new Claim(ConstantPolicies.DynamicPermissionClaimType, claim.ClaimValue));
                    }
                }
                foreach (var item in Roles)
                    Claims.Add(new Claim(ClaimTypes.Role, item.Name));
    
                return Claims;
            }

    توی پروژه ما backendبا .netoreو frontهم با react هست 

    • #
      ‫۴ سال و ۷ ماه قبل، یکشنبه ۲۹ دی ۱۳۹۸، ساعت ۲۳:۱۵
      - مطلبی که در اینجا بحث شده، در مورد کوکی‌ها هست و سیستم مبتنی بر کوکی Identity. بحث دیگری تحت عنوان «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» مختص توکن‌ها است. 
      - راه حل استانداردی که در این موارد برای JWTها استفاده می‌شود، تولید یک reference token است. مفهوم آن‌را در مطلب « امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت نهم- مدیریت طول عمر توکن‌ها » مطالعه کنید.
      - بنابراین به صورت خلاصه می‌توانید بجای کل توکن، فقط یک Guid امن کوتاه را به سمت کاربر ارسال کنید. در این حالت باید اصل توکن طولانی را در بانک اطلاعاتی ذخیره کنید. سپس زمانیکه از کاربر Guid را دریافت می‌کنید (که اینجا reference token نام دارد)، توکن متناظر با آن‌را در بانک اطلاعاتی یافته و سپس مطابق نکته‌ی OnMessageReceived مطلب «اعتبارسنجی مبتنی بر JWT »، این توکن بازیابی شده‌ی از بانک اطلاعاتی را به عنوان context.Token معرفی کنید، تا به صورت خودکار از آن استفاده شود.
  • #
    ‫۴ سال و ۷ ماه قبل، چهارشنبه ۹ بهمن ۱۳۹۸، ساعت ۲۱:۱۱
    سلام
    آیا می‌توان دسترسی‌های پویا را به گونه ای تغییر داد که علاوه بر controller بر روی razor page نیز پاسخگو باشد؟
    • #
      ‫۴ سال و ۷ ماه قبل، چهارشنبه ۹ بهمن ۱۳۹۸، ساعت ۲۳:۳۷
      در DynamicPermissionsAuthorizationHandler زمانیکه به rout data دسترسی پیدا کردید، مابقی تصمیم گیری‌ها بر اساس اطلاعات آن صورت می‌گیرند. بر روی سطر پس از آن، یک break-point را قرار دهید و محتوای این شیء را بررسی کنید. برای مثال در مورد razor pages می‌تواند چنین ساختاری را داشته باشد:
      {
        "DataTokens":{},
        "Routers":[],
        "Values":{"page":"/Index","id":"101273123"}
      }
      بنابراین بله؛ در اینجا هم می‌توان بر اساس خاصیت Values و page و سایر پارامترها، در مورد دسترسی دادن به کاربر جاری، تصمیم گیری پویایی را انجام داد.
  • #
    ‫۴ سال و ۳ ماه قبل، دوشنبه ۵ خرداد ۱۳۹۹، ساعت ۰۶:۴۷
    با سلام؛ من از DistributedCacheTicketStore استفاده کردم و به ازای هر درخواست یک دستور آپدیت بر روی جدول AppSqlCache انجام میشود:
    exec sp_executesql
        N'UPDATE [dbo].[AppSqlCache] SET ExpiresAtTime = (CASE WHEN DATEDIFF(SECOND, @UtcNow, AbsoluteExpiration) <= SlidingExpirationInSeconds THEN AbsoluteExpiration ELSE DATEADD(SECOND, SlidingExpirationInSeconds, @UtcNow) END) WHERE Id = @Id AND @UtcNow <= ExpiresAtTime AND SlidingExpirationInSeconds IS NOT NULL AND (AbsoluteExpiration IS NULL OR AbsoluteExpiration <> ExpiresAtTime) ;SELECT Id, ExpiresAtTime, SlidingExpirationInSeconds, AbsoluteExpiration, Value FROM [app].[SqlCache] WHERE Id = @Id AND @UtcNow <= ExpiresAtTime;',
        N'@Id nvarchar(449),@UtcNow datetimeoffset(7)',
        @Id = N'AuthSessionStore-380ca866e7744f569659a3c160eca7ef',
        @UtcNow = '2020-05-24 21:30:11.0837838 +00:00';
    و زمان ترافیک سایت آپدیت این جدول باعث کندی می‌شود. آیا ایده‌ی برای حال این مشکل هست؟
    • #
      ‫۴ سال و ۳ ماه قبل، دوشنبه ۵ خرداد ۱۳۹۹، ساعت ۱۵:۰۷
      مشکل؟ یا طبیعت سیستم؟
      - به ازای هر درخواست، یکبار متد RetrieveAsync کلاس DistributedCacheTicketStore فراخوانی می‌شود؛ چون شیء User موجود در HttpContext، بر اساس اطلاعات کوکی دریافتی ساخته می‌شود. اکنون که این کوکی داخل دیتابیس هست، خوب باید خوانده شود تا این شیء را تشکیل دهد.
      - فعالسازی ()services.enableImmediateLogout ، سبب فراخوانی RenewAsync کلاس DistributedCacheTicketStore به ازای هر درخواست می‌شود. اگر می‌خواهید تعداد آن‌را کمتر کنید، قابلیت logout آنی کاربران در زمان غیرفعال شدن آن‌ها در بانک اطلاعاتی را حذف کنید.
  • #
    ‫۴ سال و ۲ ماه قبل، جمعه ۲۳ خرداد ۱۳۹۹، ساعت ۱۶:۱۶
    یک نکته‌ی تکمیلی: نمونه‌ی توکار security-trimming
    tag helper سفارشی security-trimming این مطلب، نمونه‌ی توکاری به نام Condition را پیدا کرده‌است:
    <div condition="Model.Approved"> 
        <p> 
            This website has <strong surround="em">@Model.Approved</strong> been approved yet. 
            Visit www.contoso.com for more information. 
        </p> 
    </div> 
    
    <div condition="!User.Identity.IsAuthenticated">I'm not a valid user</div> 
    <div condition="User.Identity.IsAuthenticated">I can use the system</div>
  • #
    ‫۴ سال و ۲ ماه قبل، شنبه ۲۴ خرداد ۱۳۹۹، ساعت ۱۶:۴۹
    چطوری میشه از Security Trimming در vue.js استفاده کنم؟
    فرض کنید Back پروژه من Core هست و میخوام به منوها یا دکمه‌ها دسترسی پویا اعمال کنم و دسترسی‌ها رو از Identity بگیرم!!
  • #
    ‫۳ سال و ۵ ماه قبل، جمعه ۱۳ فروردین ۱۴۰۰، ساعت ۰۵:۲۷
    میشه توضیح بدید هدف از ایجاد  ConstantPolicies . DynamicPermission  چی هست؟ و اینکه چرا باید حتما در بالای کنترلرها و داخل پرانتز این عبارت به عنوان مقدار Policy قرار بگیره؟
    • #
      ‫۳ سال و ۵ ماه قبل، جمعه ۱۳ فروردین ۱۴۰۰، ساعت ۰۵:۳۸
      کلید شناسایی و فعال شدن Policy مرتبط توسط ASP.NET Core
      • #
        ‫۳ سال و ۵ ماه قبل، جمعه ۱۳ فروردین ۱۴۰۰، ساعت ۰۶:۰۰
        اگه بخوایم به صورت پیش فرض و بدون قرار دادن عبارت [Authorize(Policy = ConstantPolicies . DynamicPermission)] در بالای کنترلرها، برای کل کنترلرهای سیستم این Policy فعال بشه باید چیکار کنیم؟
  • #
    ‫۳ سال و ۵ ماه قبل، جمعه ۱۳ فروردین ۱۴۰۰، ساعت ۰۷:۲۲
    توی پروژم متد HandleRequirementAsync  در کلاس DynamicPermissionsAuthorizationHandler برای هر اکشن سه بار اجرا میشه. با بررسی call stack هم متوجه نشدم دلیلش رو. 
    دلیلش چی میتونه باشه؟ 
  • #
    ‫۳ سال و ۱ ماه قبل، پنجشنبه ۷ مرداد ۱۴۰۰، ساعت ۱۹:۳۰
    وقتی از فیلتر ConstantPolicies در کنترل هایی که در یک area  مجزا قرار دارند (تعدادی از اکشن‌ها توسط ConstantPolicies مزین شده اند )  استفاده میشود ، اکشن‌ها توسط GetAllSecuredControllerActionsWithPolicy شناسایی نمی‌شوند   و در صفحه اعمال دسترسی پویا لیست نمی‌گردند ! لطفا در این مورد راهنمایی بفرمایید.
    لازم به ذکر است مکانیزم دسترسی پویا در کنترل هایی که در area قرار نمی‌گیرند به درستی اجرا می‌شود 
    • #
      ‫۳ سال و ۱ ماه قبل، پنجشنبه ۷ مرداد ۱۴۰۰، ساعت ۲۳:۳۳
      مشکلی مشاهده نشد. ابتدا فایل جدید نمونه Areas\Identity\Controllers\DynamicPermissionsAreaSampleController.cs اضافه شد؛ با این محتوای فرضی:
      namespace ASPNETCoreIdentitySample.Areas.Identity.Controllers
      {  
          [Authorize(Policy = ConstantPolicies.DynamicPermission)]
          [Area(AreaConstants.IdentityArea)]
          [BreadCrumb(UseDefaultRouteUrl = true, Order = 0)]
          [DisplayName("کنترلر نمونه با سطح دسترسی پویا در یک ناحیه خاص")]
          public class DynamicPermissionsAreaSampleController : Controller
          {
              [DisplayName("ایندکس")]
              [BreadCrumb(Order = 1)]
              public IActionResult Index()
              {
                  return View();
              }
          }
      }
      و نتیجه‌ی آن در قسمت مشاهده‌ی دسترسی‌های پویا مشخص است:

      • #
        ‫۳ سال و ۱ ماه قبل، شنبه ۹ مرداد ۱۴۰۰، ساعت ۰۷:۱۲
        بله ، اما اگر یک area جدید دیگر تعریف شود و قصد داشته باشیم احراز هویت پویا را در آن area  نیز اعمال کنیم با این مشکل روبرو می‌شویم ! دقیقا همین کنترلر (DynamicPermissionsAreaSampleController ) را در یک area دیگر بررسی بفرمایید
        • #
          ‫۳ سال و ۱ ماه قبل، شنبه ۹ مرداد ۱۴۰۰، ساعت ۱۹:۳۵
          پروژه به‌روز شده را با توجه به دو کنترلر (^ و ^) آزمایشی جدید واقع در دو Area مجزا، اجرا و بررسی کنید:

  • #
    ‫۲ سال و ۹ ماه قبل، چهارشنبه ۱۹ آبان ۱۴۰۰، ساعت ۲۱:۱۰
    در حالت خاصی که در ادامه توضیح داده خواهد شد ممکن است بعضی از کنترلرها در لیست ظاهر نشوند، همانطور که در این کامنت به آن اشاره شده است.
    ابتدا حالتی که باعث می‌شود بعضی از کنترلرهای شما در فرم مربوط به دسترسی‌های پویا ظاهرا نشود را توضیح و سپس راه حلی که باعث حل آن می‌شود را در ادامه خواهید خواند.
    دو Area جدید به اسم‌های One , Two اضافه شدند که هر کدام شامل یک کنترلر به اسم Home  هستند (داخل هر کنترلر هم یک اکشن متد به نام Index وجود دارد )
        [Authorize(Policy = ConstantPolicies.DynamicPermission)]
        [Area("One")]
        [DisplayName("home")]
        public class HomeController : Controller
        {
            [DisplayName("ایندکس")]
            public IActionResult Index()
            {
                return View();
            }
        }

    و مشابه همین برای Area دوم :
    [Authorize(Policy = ConstantPolicies.DynamicPermission)]
        [Area("Two")]
        [DisplayName("home2")]
        public class HomeController : Controller
        {
            [DisplayName("ایندکس")]
            public IActionResult Index()
            {
                return View();
            }
        }
    خروجی نمایش داده شده در صفحه مربوطه به دسترسی‌ها به صورت زیر می‌باشد که فقط یکی از این دو کنترلر در خروجی نمایش داده می‌شود:


    بعد از بررسی کدهای مربوط به GetAllSecuredControllerActionsWithPolicy  متوجه شدم زمانی که دو کنترلر هم نام پشت سر هم قرار گرفته باشند،  کنترلر دوم بخاطر شرط زیر نادیده گرفته می‌شود:
     if (!lastControllerName.Equals(descriptor.ControllerName, StringComparison.Ordinal))
    {
    }
    با فرض اینکه اول کنترلر مربوط به Area One اجرا شود، مقدار آن در متغییر lastConrolerName قرار میگیرد، در اجرای بعدی foreach چون کنترلر بعدی به همان اسم می‌باشد از اجرا این شرط صرف نظر میکند.
    برای حل این مورد، namespace هر  کنترلر را برای منحصر بفرد کردن کنترلرها دخیل کردم:
       lastControllerName = $"{descriptor.ControllerTypeInfo.Namespace}{descriptor.ControllerName}";
    و در دستور if هم به همین صورت عمل کردم :
           var controllerName = $"{descriptor.ControllerTypeInfo.Namespace}{descriptor.ControllerName}";
            if (!lastControllerName.Equals(controllerName, StringComparison.Ordinal))

    * اگر هردو کنترلر‌های Home مربوط به area‌های one,two بعد از کنترلری به اسم Home باشند، در حالت عادی هیچ کدام از این کنترلرها در خروجی ظاهرا نخواهند شد، این مورد دقیقا مشکلی بود در پروژه جاری ام با آن برخورد کردم.
    • #
      ‫۲ سال و ۹ ماه قبل، چهارشنبه ۱۹ آبان ۱۴۰۰، ساعت ۲۱:۲۹
      آخرین مثالی را که به روز رسانی کردم، چنین مثالی را دارد (^ و ^)؛ دو کنترلر هم نام در دو ناحیه متفاوت.

      • #
        ‫۲ سال و ۹ ماه قبل، پنجشنبه ۲۰ آبان ۱۴۰۰، ساعت ۰۱:۰۳
        بله درسته، مشکلی که در بالا بیان کردم فقط زمانی اتفاق می‌افتد که کنترلرهای هم نام که از طریق زیر گرفته می‌شوند، بعد از هم قرار داشته باشند:
         actionDescriptorCollectionProvider.ActionDescriptors.Items
        دو کنترلر هم نام ( ^  و  ^ )   طبق تصویر زیر چون بعد از هم قرار نگرفته اند، مشکلی ندارند:  


        اگر مقدار actionDescriptorCollectionProvider.ActionDescriptors.Items  قبل از ورود به حلقه foreach بر اساس نام کنترلر مرتب شود( که کنترلرهای هم نام پشت سرهم  قرار گیرند) خروجی فوق را مشاهده نخواهید کرد. 

  • #
    ‫۲ سال و ۳ ماه قبل، سه‌شنبه ۲۰ اردیبهشت ۱۴۰۱، ساعت ۱۶:۰۶
    با عرض سلام. چطور باید SecurityTrimmingService را برای BlazorServer ویرایش کرد تا بتوان لیست کامپوننت‌ها را بازیابی نمود؟

    من سعی کردم روش جنابعالی را با پروژه BlazorServerCookieAuthentication ادغام کنم که اقتباسی است از ASPNETCore2CookieAuthentication. اما موفق نشدم.
  • #
    ‫۲ سال قبل، سه‌شنبه ۲۵ مرداد ۱۴۰۱، ساعت ۲۲:۲۵
    با سلام؛ من یه AuthorizationHandler سفارشی نوشتم که داخلش یه Exception صادر میکنم. همچنین یک ExceptionMiddlware سفارشی نیز نوشتم که مدیریت خطاها رو بعهده داره.  اما زمانی که از داخل AuthorizationHandler  یک exception صادر میکنم ExceptionMiddlware   فراخوانی نمیشه ! آیا نمیتوان در داخل یک middlware یه exception صادر و در exceptionMiddlware آن را دریافت کرد؟