在ASP.NET MVC中redirect未经授权的控制器

我在ASP.NET MVC中有一个控制器,我只限于pipe理员angular色:

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

如果不在Adminangular色中的用户导航到此控制器,则会看到空白屏幕。

我想要做的是将他们redirect到视图,说:“你需要在pipe理angular色能够访问此资源。”

我想到的一种做法是在IsUserInRole()上检查每个操作方法,如果不在angular色中,则返回此信息视图。 但是,我必须在每个打破DRY负责人的行动中join这个行为,而且显然很难维持。

创build一个基于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 */); } } 

您可以在自定义的AuthorizeAttribute使用可重写的HandleUnauthorizedRequest

喜欢这个:

 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(); 

“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身份validation和_isAuthorized是什么,但HttpContext.User.Identity.IsAuthenticated …

我遇到过同样的问题。 我没有找出MVC代码,而是select了一种似乎可行的廉价黑客。 在我的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) 

这个问题已经打了我好几天了,所以在find肯定的答案上面肯定的tvanfosson的答案,我认为这是值得强调的核心部分的答案,并解决一些相关的问题。

核心答案是这样的,甜美而简单:

 filterContext.Result = new HttpUnauthorizedResult(); 

在我的情况下,我从基础控制器inheritance,所以在从它inheritance的每个控制器,我重写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(); 

那么,即使有了上面的正确答案,逻辑stream程仍然在继续! 您仍然会在OnAuthorize中点击DoMoreStuff …。 所以请记住(DoMore …应该在其他地方)。

但是有了正确的答案,虽然OnAuthorize逻辑stream程一直持续到最后,然后你真的得到了你所期望的:redirect到你的login页面(如果你有一个在你的webconfig中的表单身份validation集)。

但意外的是,1)Response.Redirect(“/login”)不起作用:Action方法仍然被调用,2)FormsAuthentication.RedirectToLoginPage(); 做同样的事情:Action方法仍然被调用!

这对我来说是完全错误的,特别是对于后者:谁会认为FormsAuthentication.RedirectToLoginPage没有结束请求,或者做了什么filterContext.Result = new HttpUnauthorizedResult()的等价物呢?

你应该build立你自己的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 

当您使用Windows身份validation从开发服务器下的Visual Studio运行时,可能会得到一个空白页面( 上一主题 )。

如果您部署到IIS,则可以为特定状态代码configuration自定义错误页面,在这种情况下为401.在system.webServer下添加httpErrors:

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

然后创buildErrorController.Unauthorized方法和相应的自定义视图。

本来会留下这个评论,但我需要更多的代表,反正我只是想提到尼古拉斯彼得森,也许传递第二个参数的redirect调用,告诉它结束响应将工作。 不是最优雅的方式来处理这个,但它确实工作。

所以

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

在您的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)) } });