Cara mengarahkan ke URL login dinamis di ASP.NET MVC

96

Saya membuat situs web multi-tenancy yang menghosting halaman untuk klien. Segmen pertama URL akan menjadi string yang mengidentifikasi klien, ditentukan di Global.asax menggunakan skema perutean URL berikut:

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

Ini berfungsi dengan baik, dengan URL seperti / foo / Home / Index.

Namun, saat menggunakan atribut [Otorisasi], saya ingin mengalihkan ke halaman login yang juga menggunakan skema pemetaan yang sama. Jadi jika klien adalah foo, halaman login akan menjadi / foo / Akun / Login alih-alih pengalihan tetap / Akun / Login yang ditentukan di web.config.

MVC menggunakan HttpUnauthorizedResult untuk mengembalikan status tidak sah 401, yang saya anggap menyebabkan ASP.NET mengalihkan ke halaman yang ditentukan di web.config.

Jadi, apakah ada yang tahu cara menimpa perilaku pengalihan login ASP.NET? Atau akankah lebih baik untuk mengalihkan di MVC dengan membuat atribut otorisasi khusus?

EDIT - Jawaban: setelah menggali sumber .Net, saya memutuskan bahwa atribut otentikasi khusus adalah solusi terbaik:

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 }
                });
        }
    }
}
Mike Scott
sumber
2
melakukan hal yang hampir sama dengan perutean, jadi saya membutuhkan ini! Terima kasih!
Trevor de Koekkoek
Terima kasih, saya mencoba mencari cara untuk melakukan sesuatu yang serupa.
Kesempatan
itu memberi saya ide untuk implementasi sendiri, terima kasih banyak!
Alexander Beletsky
3
pastikan untuk menyetel area = null (atau ke area yang benar) jika menggunakan MVC 2 dan di atasnya - atau itu akan diwarisi dari laman yang Anda coba kunjungi
Simon_Weaver
Adakah cara untuk melakukan ini tanpa MVC?
DARKGuy

Jawaban:

30

Saya pikir masalah utamanya adalah bahwa jika Anda akan membonceng kelas ASP.NET FormsAuthentication built-in (dan tidak ada alasan bagus untuk tidak melakukannya), sesuatu pada akhirnya akan memanggil FormsAuthentication.RedirectToLoginPage()yang akan terjadi untuk melihat satu URL yang dikonfigurasi. Hanya ada satu URL login, dan begitulah cara mereka mendesainnya.

Masalah saya (mungkin implementasi Rube Goldberg) adalah membiarkannya dialihkan ke satu halaman login di root yang dibagikan oleh semua klien, misalnya / account / login. Halaman login ini sebenarnya tidak akan menampilkan apa pun; itu memeriksa baik parameter ReturnUrl atau beberapa nilai yang saya dapatkan dalam sesi atau cookie yang mengidentifikasi klien dan menggunakannya untuk mengeluarkan pengalihan 302 langsung ke halaman / klien / akun / login tertentu. Ini adalah pengalihan tambahan, tetapi kemungkinan tidak terlihat dan memungkinkan Anda menggunakan mekanisme pengalihan bawaan.

Opsi lainnya adalah membuat atribut khusus Anda sendiri saat Anda mendeskripsikan dan menghindari apa pun yang memanggil RedirectToLoginPage()metode di FormsAuthenticationkelas, karena Anda akan menggantinya dengan logika pengalihan Anda sendiri. (Anda dapat membuat kelas Anda sendiri yang serupa.) Karena ini adalah kelas statis, saya tidak mengetahui mekanisme apa pun yang dapat Anda gunakan untuk memasukkan antarmuka alternatif Anda sendiri dan membuatnya bekerja secara ajaib dengan atribut [Otorisasi] yang ada, yang mana pukulan, tetapi orang telah melakukan hal serupa sebelumnya .

Semoga membantu!

Nicholas Piasecki
sumber
ini mungkin pendekatan yang paling aman. membuat atribut [MyAuthorize] Anda sendiri berbahaya. kecuali bangunan Anda memverifikasi bahwa orang tidak menggunakan atribut [Otorisasi] bawaan, Anda berisiko membuat orang (atau diri Anda sendiri) lupa dan menggunakan yang salah
Simon_Weaver
Dalam beberapa kasus, ini dapat membantu untuk menimpa Application_AuthenticateRequest(lihat jawaban saya di bawah).
turdus-merula
41

Dalam versi RTM ASP.NET MVC, properti Batal hilang. Kode ini bekerja dengan 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 }
                        });
            }
        }
    }
}

Sunting: Anda mungkin ingin menonaktifkan bentuk default otentikasi loginUrl di web.config - jika seseorang lupa Anda memiliki atribut khusus dan menggunakan atribut bawaan [Otorisasi] karena kesalahan.

Ubah nilai di web.config:

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

Kemudian buat metode tindakan 'ERROR' yang mencatat kesalahan dan mengarahkan pengguna ke halaman login paling umum yang Anda miliki.

pengguna134936
sumber
2
pastikan untuk menambahkan {area, null} ke kamus (atau apa pun sebutan area Anda) jika menggunakan MVC 2 dan di atasnya - atau itu akan diwarisi dari laman yang Anda coba kunjungi
Simon_Weaver
2

Solusi saya untuk masalah ini adalah ActionResultkelas khusus :

    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; }
    }
Kieron
sumber
0

Namun, jika seseorang memutuskan untuk menggunakan built-in ASP.NET FormsAuthentication, satu dapat overide Application_AuthenticateRequestdi Global.asax.cssebagai berikut:

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;
        }
    }
}
turdus-merula
sumber