在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(您的错误视图)。
编辑 :我有几个博客文章进入更多的细节:
- http://farm-fresh-code.blogspot.com/2011/03/revisiting-custom-authorization-in.html
- http://farm-fresh-code.blogspot.com/2009/11/customizing-authorization-in-aspnet-mvc.html
例:
[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)) } });