วิธีเปลี่ยนเส้นทางไปยัง URL ล็อกอินแบบไดนามิกใน ASP.NET MVC


96

ฉันกำลังสร้างเว็บไซต์สำหรับผู้เช่าหลายรายซึ่งโฮสต์เพจสำหรับลูกค้า ส่วนแรกของ URL จะเป็นสตริงที่ระบุไคลเอ็นต์ซึ่งกำหนดไว้ใน Global.asax โดยใช้โครงร่างการกำหนดเส้นทาง URL ต่อไปนี้:

"{client}/{controller}/{action}/{id}"

ใช้งานได้ดีกับ URL เช่น / foo / Home / Index

อย่างไรก็ตามเมื่อใช้แอตทริบิวต์ [Authorize] ฉันต้องการเปลี่ยนเส้นทางไปยังหน้าการเข้าสู่ระบบซึ่งใช้รูปแบบการแมปเดียวกันด้วย ดังนั้นหากลูกค้าเป็น foo หน้าล็อกอินจะเป็น / foo / Account / Login แทนการเปลี่ยนเส้นทางคงที่ / Account / Login ที่กำหนดไว้ใน web.config

MVC ใช้ HttpUnauthorizedResult เพื่อส่งคืนสถานะ 401 ที่ไม่ได้รับอนุญาตซึ่งฉันคิดว่าเป็นสาเหตุให้ ASP.NET เปลี่ยนเส้นทางไปยังหน้าที่กำหนดใน web.config

ไม่มีใครรู้วิธีการแทนที่พฤติกรรมการเปลี่ยนเส้นทางการเข้าสู่ระบบ ASP.NET? หรือจะดีกว่าถ้าเปลี่ยนเส้นทางใน MVC โดยสร้างแอตทริบิวต์การให้สิทธิ์แบบกำหนดเอง

แก้ไข - คำตอบ:หลังจากขุดลงในซอร์ส. Net ฉันตัดสินใจว่าแอตทริบิวต์การตรวจสอบความถูกต้องแบบกำหนดเองเป็นทางออกที่ดีที่สุด:

public class ClientAuthorizeAttribute: AuthorizeAttribute
{
    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        base.OnAuthorization( filterContext );

        if (filterContext.Cancel && filterContext.Result is HttpUnauthorizedResult )
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary
                {
                    { "client", filterContext.RouteData.Values[ "client" ] },
                    { "controller", "Account" },
                    { "action", "Login" },
                    { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                });
        }
    }
}

2
ทำเกือบจะเหมือนกันทุกประการกับการกำหนดเส้นทางดังนั้นฉันจึงต้องการสิ่งนี้! ขอบคุณ!
Trevor de Koekkoek

ขอบคุณฉันพยายามหาวิธีทำสิ่งที่คล้ายกัน
โอกาส

มันทำให้ฉันมีความคิดในการนำไปใช้เองขอบคุณมาก!
Alexander Beletsky

3
อย่าลืมตั้งค่า area = null (หรือเป็นพื้นที่ที่ถูกต้อง) หากใช้ MVC 2 ขึ้นไป - มิฉะนั้นจะได้รับการสืบทอดจากหน้าที่คุณพยายามเข้าชม
Simon_Weaver

มีวิธีใดบ้างที่จะทำสิ่งนี้โดยไม่ใช้ MVC?
DARKGuy

คำตอบ:


30

ฉันคิดว่าปัญหาหลักคือถ้าคุณจะไป piggyback ในคลาส ASP.NET FormsAuthentication ในตัว (และไม่มีเหตุผลที่ดีที่คุณไม่ควร) มีบางอย่างในตอนท้ายของวันที่จะโทรFormsAuthentication.RedirectToLoginPage()ไป เพื่อดู URL ที่กำหนดค่าไว้ มี URL สำหรับเข้าสู่ระบบเพียงรายการเดียวและนั่นเป็นเพียงวิธีที่พวกเขาออกแบบ

การแทงที่ปัญหาของฉัน (อาจเป็นการใช้งาน Rube Goldberg) คือปล่อยให้มันเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบเดียวที่รูทที่ใช้ร่วมกันโดยไคลเอนต์ทั้งหมด หน้าเข้าสู่ระบบนี้จะไม่แสดงอะไรเลย ตรวจสอบพารามิเตอร์ ReturnUrl หรือค่าบางอย่างที่ฉันได้รับในเซสชันหรือคุกกี้ที่ระบุไคลเอนต์และใช้เพื่อออกการเปลี่ยนเส้นทาง 302 ทันทีไปยังหน้าเฉพาะ / ลูกค้า / บัญชี / ล็อกอิน เป็นการเปลี่ยนเส้นทางแบบพิเศษ แต่อาจไม่สามารถสังเกตเห็นได้และช่วยให้คุณใช้กลไกการเปลี่ยนเส้นทางในตัว

อีกทางเลือกหนึ่งคือการสร้างแอตทริบิวต์ที่กำหนดเองตามที่คุณอธิบายและหลีกเลี่ยงสิ่งที่เรียกใช้RedirectToLoginPage()เมธอดในFormsAuthenticationคลาสเนื่องจากคุณจะแทนที่ด้วยตรรกะการเปลี่ยนเส้นทางของคุณเอง (คุณอาจสร้างคลาสของคุณเองที่คล้ายกัน) เนื่องจากเป็นคลาสคงที่ฉันไม่ทราบกลไกใด ๆ ที่คุณสามารถฉีดอินเทอร์เฟซทางเลือกของคุณเองและให้มันทำงานอย่างน่าอัศจรรย์กับแอตทริบิวต์ [Authorize] ที่มีอยู่ซึ่ง พัด แต่คนที่ได้ทำสิ่งที่คล้ายกันก่อน

หวังว่าจะช่วยได้!


นี่อาจเป็นแนวทางที่ปลอดภัยที่สุด การสร้างแอตทริบิวต์ [MyAuthorize] ของคุณเองนั้นอันตราย เว้นแต่ว่าบิลด์ของคุณจะยืนยันว่าผู้ใช้ไม่ได้ใช้แอตทริบิวต์ [Authorize] ในตัวคุณอาจเสี่ยงต่อผู้อื่น (หรือตัวคุณเอง) ลืมและใช้อันผิด
Simon_Weaver

ในบางกรณีการลบล้างอาจเป็นประโยชน์Application_AuthenticateRequest(ดูคำตอบของฉันด้านล่าง)
turdus-merula

41

ใน ASP.NET MVC เวอร์ชัน RTM คุณสมบัติยกเลิกขาดหายไป รหัสนี้ใช้ได้กับ ASP.NET MVC RTM:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Resources;

namespace ePegasus.Web.ActionFilters
{
    public class CustomAuthorize : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            if (filterContext.Result is HttpUnauthorizedResult)
            {
                filterContext.Result = new RedirectToRouteResult(
                    new System.Web.Routing.RouteValueDictionary
                        {
                                { "langCode", filterContext.RouteData.Values[ "langCode" ] },
                                { "controller", "Account" },
                                { "action", "Login" },
                                { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                        });
            }
        }
    }
}

แก้ไข:คุณอาจต้องการปิดใช้งาน loginUrl การพิสูจน์ตัวตนเริ่มต้นใน web.config - ในกรณีที่มีคนลืมว่าคุณมีแอตทริบิวต์ที่กำหนดเองและใช้แอตทริบิวต์ [Authorize] ในตัวโดยไม่ได้ตั้งใจ

แก้ไขค่าใน web.config:

 <forms loginUrl="~/Account/ERROR" timeout="2880" />

จากนั้นสร้างวิธีการดำเนินการ 'ERROR' ที่บันทึกข้อผิดพลาดและเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าการเข้าสู่ระบบทั่วไปที่คุณมี


2
อย่าลืมเพิ่ม {area, null} ลงในพจนานุกรม (หรือพื้นที่ของคุณเรียกว่าอะไรก็ตาม) หากใช้ MVC 2 ขึ้นไป - มิฉะนั้นจะได้รับการสืบทอดมาจากหน้าที่คุณพยายามเข้าชม
Simon_Weaver

2

วิธีแก้ปัญหาของฉันคือ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; }
    }

0

ยังถ้าหนึ่งตัดสินใจที่จะใช้ในตัว ASP.NET นั้น FormsAuthentication หนึ่งสามารถทับApplication_AuthenticateRequestในGlobal.asax.csดังนี้

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    string url = Request.RawUrl;

    if (url.Contains(("Account/Login"))
    {
        return;
    }

    if (Context.User == null)
    {
        // Your custom tenant-aware logic
        if (url.StartsWith("/foo"))
        {
            // Your custom login page.
            Response.Redirect("/foo/Account/Login");
            Response.End();
            return;
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.