91

我正在創建一個承載客戶端頁面的多租戶網站。 URL的第一部分將是一個字符串,識別客戶端,使用下面的URL路由方案在Global.asax中定義:如何重定向到ASP.NET MVC中的動態登錄URL

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

這工作得很好,用的URL如/富/首頁/索引。

但是,當使用[Authorize]屬性時,我想重定向到也使用相同映射方案的登錄頁面。因此,如果客戶端是foo,登錄頁面將是/ foo/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

具有路由做幾乎同樣的事情,所以我需要這個!謝謝! – 2008-12-27 21:20:41

+0

謝謝,我試圖弄清楚如何做類似的事情。 – Chance 2010-02-03 02:34:06

+0

它給了我自己實現的想法,非常感謝! – 2010-10-31 16:47:08

回答

30

我認爲主要的問題是,如果你打算以搭載內置的ASP.NET FormsAuthentication類(並且沒有理由不應該這樣做),那麼在一天結束時將調用FormsAuthentication.RedirectToLoginPage()這將查看一個配置的URL。以前只有一個登錄網址,這就是他們設計的方式。

我對這個問題(可能是Rube Goldberg實現)的攻擊是讓它重定向到所有客戶端共享的根目錄中的單個登錄頁面,比如/ account/login。此登錄頁面實際上不會顯示任何內容;它會檢查ReturnUrl參數或我在會話中獲得的某個值,或者是一個標識客戶端的cookie,並使用它來立即發送302到指定/客戶端/帳戶/登錄頁面的重定向。這是一個額外的重定向,但可能不明顯,它允許您使用內置的重定向機制。

另一種選擇是按照您的描述創建自己的自定義屬性,並避免在FormsAuthentication類中調用RedirectToLoginPage()方法的任何內容,因爲您將用自己的重定向邏輯替換它。 (你可以創建自己的類。)由於它是一個靜態類,我沒有意識到任何機制,你可以注入自己的替代接口,並讓它神奇地與現有的[Authorize]屬性一起工作,吹,但people have done similar things before

希望有幫助!

40

在RTM版本的ASP.NET MVC中,Cancel屬性丟失。該代碼可以使用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 } 
         }); 
      } 
     } 
    } 
} 

編輯:您可能希望禁用web.config中的默認窗體身份驗證loginUrl - 萬一有人忘記你有一個自定義屬性和用途錯誤地內置了[Authorize]屬性。

修改網頁中的值。配置:

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

然後制定行動方法「錯誤」是記錄錯誤,並將用戶重定向到您擁有最通用的登錄頁面。

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; } 
    }