2009-06-10 106 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)) 
    } 
});