2009-06-10 212 views
70

我在ASP.NET MVC控制器,我已經限制到管理角色:重定向擅自控制器ASP.NET MVC

[Authorize(Roles = "Admin")] 
public class TestController : Controller 
{ 
    ... 

如果誰不是在管理角色的用戶導航到該控制器他們迎接一個空白的屏幕。

我想要做的就是將它們重定向到View,其中顯示「您需要擔任Admin角色才能訪問此資源。」

這樣做的一種方法,我想到的是檢查IsUserInRole()上的每個操作方法,如果不在角色中,則返回此信息視圖。但是,我必須在每個打破DRY負責人的操作中加入這個操作,而且顯然很難維護。

回答

67

創建基於AuthorizeAttribute定製的授權屬性,並覆蓋OnAuthorization執行要如何完成它的檢查。通常,如果授權檢查失敗,AuthorizeAttribute會將篩選結果設置爲HttpUnauthorizedResult。您可以將它設置爲ViewResult(您的錯誤視圖)。

編輯:我有幾個博客帖子說進入更詳細:

例子:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
    public class MasterEventAuthorizationAttribute : AuthorizeAttribute 
    { 
     /// <summary> 
     /// The name of the master page or view to use when rendering the view on authorization failure. Default 
     /// is null, indicating to use the master page of the specified view. 
     /// </summary> 
     public virtual string MasterName { get; set; } 

     /// <summary> 
     /// The name of the view to render on authorization failure. Default is "Error". 
     /// </summary> 
     public virtual string ViewName { get; set; } 

     public MasterEventAuthorizationAttribute() 
      : base() 
     { 
      this.ViewName = "Error"; 
     } 

     protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) 
     { 
      validationStatus = OnCacheAuthorization(new HttpContextWrapper(context)); 
     } 

     public override void OnAuthorization(AuthorizationContext filterContext) 
     { 
      if (filterContext == null) 
      { 
       throw new ArgumentNullException("filterContext"); 
      } 

      if (AuthorizeCore(filterContext.HttpContext)) 
      { 
       SetCachePolicy(filterContext); 
      } 
      else if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
      { 
       // auth failed, redirect to login page 
       filterContext.Result = new HttpUnauthorizedResult(); 
      } 
      else if (filterContext.HttpContext.User.IsInRole("SuperUser")) 
      { 
       // is authenticated and is in the SuperUser role 
       SetCachePolicy(filterContext); 
      } 
      else 
      { 
       ViewDataDictionary viewData = new ViewDataDictionary(); 
       viewData.Add("Message", "You do not have sufficient privileges for this operation."); 
       filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData }; 
      } 

     } 

     protected void SetCachePolicy(AuthorizationContext filterContext) 
     { 
      // ** IMPORTANT ** 
      // Since we're performing authorization at the action level, the authorization code runs 
      // after the output caching module. In the worst case this could allow an authorized user 
      // to cause the page to be cached, then an unauthorized user would later be served the 
      // cached page. We work around this by telling proxies not to cache the sensitive page, 
      // then we hook our custom authorization code into the caching mechanism so that we have 
      // the final say on whether a page should be served from the cache. 
      HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache; 
      cachePolicy.SetProxyMaxAge(new TimeSpan(0)); 
      cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */); 
     } 


    } 
+1

我不認爲有一個我可以去的鏈接,這個鏈接把它分解成更容易遵循推理? – Maslow 2010-01-18 02:12:16

1

你應該建立你自己的Authorize-filter屬性。

這裏是我的學習;)

Public Class RequiresRoleAttribute : Inherits ActionFilterAttribute 
    Private _role As String 

    Public Property Role() As String 
     Get 
      Return Me._role 
     End Get 
     Set(ByVal value As String) 
      Me._role = value 
     End Set 
    End Property 

    Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext) 
     If Not String.IsNullOrEmpty(Me.Role) Then 
      If Not filterContext.HttpContext.User.Identity.IsAuthenticated Then 
       Dim redirectOnSuccess As String = filterContext.HttpContext.Request.Url.AbsolutePath 
       Dim redirectUrl As String = String.Format("?ReturnUrl={0}", redirectOnSuccess) 
       Dim loginUrl As String = FormsAuthentication.LoginUrl + redirectUrl 

       filterContext.HttpContext.Response.Redirect(loginUrl, True) 
      Else 
       Dim hasAccess As Boolean = filterContext.HttpContext.User.IsInRole(Me.Role) 
       If Not hasAccess Then 
        Throw New UnauthorizedAccessException("You don't have access to this page. Only " & Me.Role & " can view this page.") 
       End If 
      End If 
     Else 
      Throw New InvalidOperationException("No Role Specified") 
     End If 

    End Sub 
End Class 
+0

這似乎重定向,但它似乎也運行在原來的操作方法全部第一。 – 2013-10-01 19:17:17

+0

而不是做一個重定向,你應該做`filterContext.Result = new RedirectResult(loginUrl)` – 2013-10-01 19:23:19

5

我有同樣的問題。我沒有找出MVC代碼,而是選擇了一種似乎可行的廉價黑客。在我的Global.asax類:

member x.Application_EndRequest() = 
    if x.Response.StatusCode = 401 then 
     let redir = "?redirectUrl=" + Uri.EscapeDataString x.Request.Url.PathAndQuery 
     if x.Request.Url.LocalPath.ToLowerInvariant().Contains("admin") then 
      x.Response.Redirect("/Login/Admin/" + redir) 
     else 
      x.Response.Redirect("/Login/Login/" + redir) 
9

通過「tvanfosson」的代碼是給我的「執行子請求錯誤」。我已經改變了OnAuthorization這樣的:

public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if (!_isAuthorized) 
     { 
      filterContext.Result = new HttpUnauthorizedResult(); 
     } 
     else if (filterContext.HttpContext.User.IsInRole("Administrator") || filterContext.HttpContext.User.IsInRole("User") || filterContext.HttpContext.User.IsInRole("Manager")) 
     { 
      // is authenticated and is in one of the roles 
      SetCachePolicy(filterContext); 
     } 
     else 
     { 
      filterContext.Controller.TempData.Add("RedirectReason", "You are not authorized to access this page."); 
      filterContext.Result = new RedirectResult("~/Error"); 
     } 
    } 

這工作得很好,我在錯誤頁面上顯示TempData。感謝代碼片段的「tvanfosson」。我正在使用Windows身份驗證和_isAuthorized是什麼,但HttpContext.User.Identity.IsAuthenticated ...

22

您可以使用重寫HandleUnauthorizedRequest工作,你的自定義AuthorizeAttribute

像這裏面:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
{ 
    // Returns HTTP 401 by default - see HttpUnauthorizedResult.cs. 
    filterContext.Result = new RedirectToRouteResult(
    new RouteValueDictionary 
    { 
     { "action", "YourActionName" }, 
     { "controller", "YourControllerName" }, 
     { "parameterName", "YourParameterValue" } 
    }); 
} 

你也可以做這樣的事情:

private class RedirectController : Controller 
{ 
    public ActionResult RedirectToSomewhere() 
    { 
     return RedirectToAction("Action", "Controller"); 
    } 
} 

現在,您可以使用它您的HandleUnauthorizedRequest這種方法:

filterContext.Result = (new RedirectController()).RedirectToSomewhere(); 
2

這個問題已經讓我有些日子了,所以在尋找答案肯定與tvanfosson上面的答案一致時,我認爲值得強調答案的核心部分,並解決一些相關的問題。

的核心答案是這樣的,甜的和簡單的:

filterContext.Result = new HttpUnauthorizedResult(); 

在我的情況下,我從一個基本控制器繼承,所以在每一個從它繼承控制器我重寫OnAuthorize:

protected override void OnAuthorization(AuthorizationContext filterContext) 
{ 
    base.OnAuthorization(filterContext); 
    YourAuth(filterContext); // do your own authorization logic here 
} 

問題是,在'YourAuth'中,我嘗試了兩件我認爲不僅可行的事情,而且還會立即終止請求。那麼,這不是它的工作原理。所以,第一,兩件事情,不工作,意外:

filterContext.RequestContext.HttpContext.Response.Redirect("/Login"); // doesn't work! 
FormsAuthentication.RedirectToLoginPage(); // doesn't work! 

不僅那些沒有工作,他們不兩端的請求。這意味着:

if (!success) { 
    filterContext.Result = new HttpUnauthorizedResult(); 
} 
DoMoreStuffNowThatYouThinkYourAuthorized(); 

好了,即使有以上正確答案,邏輯流依然延續!您仍然會在OnAuthorize中點擊DoMoreStuff ...。所以請記住(DoMore ...應該在其他地方)。

但有了正確的答案,雖然OnAuthorize邏輯流程一直持續到結束,然後你確實得到你所期望的:重定向到你的登錄頁面(如果你在你的webconfig中有一個設置在Forms auth中) 。

但出乎意料的是, 1)的Response.Redirect( 「/登錄」)不工作:操作方法仍然被調用,並 2)FormsAuthentication.RedirectToLoginPage();做同樣的事情:Action方法仍然被調用!

對我來說,這似乎完全錯誤,特別是對於後者:誰會認爲FormsAuthentication.RedirectToLoginPage不會結束請求,或者執行上述filterContext.Result = new HttpUnauthorizedResult()的等價操作?

1

本來會留下這個評論,但我需要更多的代表,無論如何,我只是想提到尼古拉斯彼得森,也許傳遞第二個參數到重定向調用告訴它結束響應會起作用。不是處理這個問題的最優雅的方式,但它確實有效。

所以

filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true); 

,而不是

filterContext.RequestContext.HttpContext.Response.Redirect("/Login); 

所以你必須在這個控制器:

protected override void OnAuthorization(AuthorizationContext filterContext) 
{ 
     if(!User.IsInRole("Admin") 
     { 
      base.OnAuthorization(filterContext); 
      filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true); 
     } 
} 
1

也許當你從Visual Studio中運行你得到一個空白頁正在開發使用Windows身份驗證的服務器(previous topic)。

如果你部署到IIS可以配置特定狀態代碼自定義錯誤頁,在這種情況下,401添加httpErrors system.webServer下:

<httpErrors> 
    <remove statusCode="401" /> 
    <error statusCode="401" path="/yourapp/error/unauthorized" responseMode="Redirect" /> 
</httpErrors> 

然後創建ErrorController.Unauthorized方法和相應的自定義視圖。

-1

在你Startup.Auth.cs文件中加入這一行:

LoginPath = new PathString("/Account/Login"), 

例子:

// Enable the application to use a cookie to store information for the signed in user 
// and to use a cookie to temporarily store information about a user logging in with a third party login provider 
// Configure the sign in cookie 
app.UseCookieAuthentication(new CookieAuthenticationOptions 
{ 
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
    LoginPath = new PathString("/Account/Login"), 
    Provider = new CookieAuthenticationProvider 
    { 
     // Enables the application to validate the security stamp when the user logs in. 
     // This is a security feature which is used when you change a password or add an external login to your account. 
     OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
     validateInterval: TimeSpan.FromMinutes(30), 
     regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) 
    } 
});