asp.net-mvc - لماذا يقوم AuthorizeAttribute بإعادة التوجيه إلى صفحة تسجيل الدخول للمصادقة وفشل التخويل؟




authentication authorization (6)

في ASP.NET MVC ، يمكنك ترميز طريقة وحدة تحكم مع AuthorizeAttribute ، مثل هذا:

[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
    // ...
}

وهذا يعني أنه إذا لم يكن المستخدم الذي قام بتسجيل الدخول حاليًا في الدور "CanDeleteTags" ، فلن يتم استدعاء طريقة وحدة التحكم مطلقًا.

لسوء الحظ ، للإخفاقات ، يقوم AuthorizeAttribute بإرجاع HttpUnauthorizedResult ، والذي يقوم دائمًا بإرجاع رمز الحالة HTTP 401. يؤدي هذا إلى إعادة توجيه إلى صفحة تسجيل الدخول.

إذا لم يقم المستخدم بتسجيل الدخول ، فهذا أمر منطقي. ومع ذلك ، إذا كان المستخدم قد قام بالفعل بتسجيل الدخول ، ولكنه ليس في الدور المطلوب ، فمن المربك إعادته إلى صفحة تسجيل الدخول.

يبدو أن AuthorizeAttribute يخلط بين المصادقة والتخويل.

هذا يبدو وكأنه جزء من الرقابة في ASP.NET MVC ، أو هل أنا في عداد المفقودين شيء؟

لقد اضطررت لطهي DemandRoleAttribute الذي يفصل بين الاثنين. عند عدم مصادقة المستخدم ، يتم إرجاع HTTP 401 ، وإرساله إلى صفحة تسجيل الدخول. عندما يقوم المستخدم بتسجيل الدخول ، ولكن ليس في الدور المطلوب ، يقوم بإنشاء NotAuthorizedResult بدلاً من ذلك. حاليا هذا يعيد توجيه إلى صفحة خطأ.

بالتأكيد لم يكن عليّ فعل هذا؟


Answers

حاول ذلك في معالج Application_EndRequest الخاص بملف Global.ascx الخاص بك

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
    HttpContext.Current.Response.ClearContent();
    Response.Redirect("~/AccessDenied.aspx");
}

لسوء الحظ ، أنت تتعامل مع السلوك الافتراضي لمصادقة نماذج ASP.NET. هناك حل بديل (لم أجربه) تمت مناقشته هنا:

http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

(لا يقتصر على MVC)

أعتقد أن الحل الأفضل في معظم الحالات هو تقييد الوصول إلى الموارد غير المصرح بها قبل محاولة المستخدم الوصول إلى هناك. من خلال إزالة / إزالة الرابط أو الزر الذي قد يأخذهم إلى هذه الصفحة غير المصرح بها.

قد يكون من الجيد وجود معلمة إضافية على السمة لتحديد مكان إعادة توجيه مستخدم غير مصرح به. ولكن في هذه الأثناء ، أنظر إلى AuthorizeAttribute كشبكة أمان.


اعتقدت دائما أن هذا له معنى. إذا قمت بتسجيل الدخول وحاولت أن تضغط على صفحة تتطلب دورًا لا تملكه ، فستتم إعادة توجيهك إلى شاشة تسجيل الدخول تطلب منك تسجيل الدخول مع مستخدم له دور.

يمكنك إضافة منطق إلى صفحة تسجيل الدخول التي تتحقق لمعرفة ما إذا كان المستخدم قد تمت مصادقته بالفعل. يمكنك إضافة رسالة ودية تشرح سبب رجوعهم إلى هناك مرة أخرى.


أضف هذا إلى وظيفة تسجيل الدخول إلى Page_Load الخاصة بك:

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

عند إعادة توجيه المستخدم إلى هناك ولكن تم تسجيل الدخول بالفعل ، فإنه يعرض الصفحة غير المصرح بها. إذا لم يتم تسجيل الدخول ، فإنه يقع من خلال ويظهر صفحة تسجيل الدخول.


عندما تم تطويره لأول مرة ، كان System.Web.Mvc.AuthorizeAttribute يقوم بالشيء الصحيح - المراجعات القديمة لمواصفات HTTP المستخدمة رمز الحالة 401 لكل من "غير المصرح به" و "غير المصادق".

من المواصفات الأصلية:

إذا كان الطلب يتضمن بالفعل بيانات اعتماد التخويل ، فإن استجابة 401 تشير إلى أنه تم رفض الترخيص لبيانات الاعتماد هذه.

في الواقع ، يمكنك أن ترى الارتباك هناك - تستخدم كلمة "تفويض" عندما تعني "المصادقة". في الممارسة اليومية ، ومع ذلك ، فمن المنطقي إعادة 403 ممنوع عندما يتم توثيق المستخدم ولكن غير مصرح به. من غير المحتمل أن يكون لدى المستخدم مجموعة ثانية من بيانات الاعتماد التي من شأنها منحهم إمكانية الوصول - تجربة المستخدم السيئة في كل مكان.

فكّر في معظم أنظمة التشغيل - عند محاولة قراءة ملف ليس لديك إذن بالدخول إليه ، لا تظهر لك شاشة تسجيل الدخول!

لحسن الحظ ، تم تحديث مواصفات HTTP (يونيو 2014) لإزالة الغموض.

من "بروتوكول نقل النص التشعبي (HTTP / 1.1): المصادقة" (RFC 7235):

يشير رمز الحالة 401 (غير مصرح به) إلى أنه لم يتم تطبيق الطلب لأنه يفتقر إلى بيانات اعتماد المصادقة الصالحة للمورد الهدف.

من "Hypertext Transfer Protocol (HTTP / 1.1): Semantics and Content" (RFC 7231):

يشير رمز الحالة 403 (محظور) إلى أن الخادم يفهم الطلب ولكنه يرفض اعتماده.

ومن المثير للاهتمام ، أنه في الوقت الذي تم فيه إصدار ASP.NET MVC 1 كان تصرف AuthorizeAttribute صحيحًا. الآن ، السلوك غير صحيح - تم إصلاح مواصفات HTTP / 1.1.

بدلاً من محاولة تغيير عمليات إعادة توجيه صفحات تسجيل الدخول إلى ASP.NET ، يكون من الأسهل حل المشكلة عند المصدر فقط. يمكنك إنشاء سمة جديدة بنفس الاسم ( AuthorizeAttribute ) في مساحة الاسم الافتراضية لموقعك على الويب (وهذا أمر مهم جدًا) ، ثم يقوم المترجم تلقائيًا بالتقاطه بدلاً من القياسي القياسي لشركة MVC. بالطبع ، يمكنك دائمًا إعطاء السمة اسمًا جديدًا إذا كنت تفضل استخدام هذا النهج.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

كان الحل لهذه المشكلة هو فئة ActionResult مخصصة:

    sealed public class RequiresLoginResult : ActionResult
    {
        override public void ExecuteResult (ControllerContext context)
        {
            var response = context.HttpContext.Response;

            var url = FormsAuthentication.LoginUrl;
            if (!string.IsNullOrWhiteSpace (url))
                url += "?returnUrl=" + HttpUtility.UrlEncode (ReturnUrl);

            response.Clear ();
            response.StatusCode = 302;
            response.RedirectLocation = url;
        }

        public RequiresLoginResult (string returnUrl = null)
        {
            ReturnUrl = returnUrl;
        }

        string ReturnUrl { get; set; }
    }




asp.net-mvc authentication authorization