简体   繁体   English

在ASP.NET MVC中重定向未授权的控制器

[英]Redirecting unauthorized controller in ASP.NET MVC

I have a controller in ASP.NET MVC that I've restricted to the admin role: 我在ASP.NET MVC中有一个控制器,我已将其限制为管理员角色:

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

If a user who is not in the Admin role navigates to this controller they are greeted with a blank screen. 如果不在Admin角色中的用户导航到此控制器,则会显示空白屏幕。

What I would like to do is redirect them to View that says "you need to be in the Admin role to be able to access this resource." 我想要做的是将它们重定向到View,其中显示“您需要处于Admin角色才能访问此资源”。

One way of doing this that I've thought of is to have a check in each action method on IsUserInRole() and if not in role then return this informational view. 我想到的一种方法是在IsUserInRole()上检查每个操作方法,如果不在角色中,则返回此信息视图。 However, I'd have to put that in each Action which breaks the DRY principal and is obviously cumbersome to maintain. 但是,我必须在每个打破DRY主体的Action中加入它,显然很难维护。

Create a custom authorization attribute based on AuthorizeAttribute and override OnAuthorization to perform the check how you want it done. 基于AuthorizeAttribute创建自定义授权属性并覆盖OnAuthorization以执行检查完成的方式。 Normally, AuthorizeAttribute will set the filter result to HttpUnauthorizedResult if the authorization check fails. 通常,如果授权检查失败,AuthorizeAttribute会将过滤结果设置为HttpUnauthorizedResult。 You could have it set it to a ViewResult (of your Error view) instead. 您可以将其设置为ViewResult(Error视图)。

EDIT : I have a couple of blog posts that go into more detail: 编辑 :我有一些博客文章更详细:

Example: 例:

    [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 */);
        }


    }

You can work with the overridable HandleUnauthorizedRequest inside your custom AuthorizeAttribute 您可以在自定义AuthorizeAttribute使用可HandleUnauthorizedRequest

Like this: 像这样:

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

You can also do something like this: 你也可以这样做:

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

Now you can use it in your HandleUnauthorizedRequest method this way: 现在,您可以通过以下方式在HandleUnauthorizedRequest方法中使用它:

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

The code by "tvanfosson" was giving me "Error executing Child Request".. I have changed the OnAuthorization like this: “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");
        }
    }

This works well and I show the TempData on error page. 这很好用,我在错误页面上显示TempData。 Thanks to "tvanfosson" for the code snippet. 感谢代码片段的“tvanfosson”。 I am using windows authentication and _isAuthorized is nothing but HttpContext.User.Identity.IsAuthenticated... 我正在使用Windows身份验证,_isAuthorized只是HttpContext.User.Identity.IsAuthenticated ...

I had the same issue. 我遇到过同样的问题。 Rather than figure out the MVC code, I opted for a cheap hack that seems to work. 我没有找出MVC代码,而是选择了一个看似有效的廉价黑客。 In my Global.asax class: 在我的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)

This problem has hounded me for some days now, so on finding the answer that affirmatively works with tvanfosson's answer above, I thought it would be worthwhile to emphasize the core part of the answer, and address some related catch ya's. 这个问题已经困扰了我好几天了,所以在找到肯定与tvanfosson上面的答案一致的答案时,我认为值得强调答案的核心部分,并解决一些相关的问题。

The core answer is this, sweet and simple: 核心答案是这样,甜蜜而简单:

filterContext.Result = new HttpUnauthorizedResult();

In my case I inherit from a base controller, so in each controller that inherits from it I override OnAuthorize: 在我的情况下,我从一个基本控制器继承,所以在从它继承的每个控制器中,我重写OnAuthorize:

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

The problem was that in 'YourAuth', I tried two things that I thought would not only work, but would also immediately terminate the request. 问题是在'YourAuth'中,我尝试了两件我认为不仅可以工作的东西,但也会立即终止请求。 Well, that is not how it works. 嗯,这不是它的工作原理。 So first, the two things that DO NOT work, unexpectedly: 首先,出乎意料的是,这两件事不起作用:

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

Not only do those not work, they don't end the request either. 这些不仅不起作用,也不会结束请求。 Which means the following: 这意味着以下内容:

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

Well, even with the correct answer above, the flow of logic still continues! 好吧,即使上面的答案正确,逻辑的流程仍在继续! You will still hit DoMoreStuff... within OnAuthorize. 你仍然会在OnAuthorize中点击DoMoreStuff。 So keep that in mind (DoMore... should be in an else therefore). 所以记住这一点(DoMore ......因此应该在其他地方)。

But with the correct answer, while OnAuthorize flow of logic continues till the end still, after that you really do get what you expect: a redirect to your login page (if you have one set in Forms auth in your webconfig). 但正确的答案是,OnAuthorize逻辑流程一直持续到最后仍然存在,之后你真的得到了你所期望的:重定向到你的登录页面(如果你在webconfig中有一个Forms auth中的一个)。

But unexpectedly, 1) Response.Redirect("/Login") does not work: the Action method still gets called, and 2) FormsAuthentication.RedirectToLoginPage(); 但出乎意料的是,1)Response.Redirect(“/ Login”)不起作用:Action方法仍然被调用,2)FormsAuthentication.RedirectToLoginPage(); does the same thing: the Action method still gets called! 做同样的事情:Action方法仍然被调用!

Which seems totally wrong to me, particularly with the latter: who would have thought that FormsAuthentication.RedirectToLoginPage does not end the request, or do the equivalant above of what filterContext.Result = new HttpUnauthorizedResult() does? 这对我来说似乎是完全错误的,特别是对于后者:谁会认为FormsAuthentication.RedirectToLoginPage不会结束请求,或者做上面的filterContext.Result = new HttpUnauthorizedResult()的等价物呢?

Would have left this as a comment but I need more rep, anyways I just wanted to mention to Nicholas Peterson that perhaps passing the second argument to the Redirect call to tell it to end the response would have worked. 本来可以留下这个评论,但我需要更多的代表,反正我只想提到Nicholas Peterson,或许可以将第二个参数传递给Redirect调用,告诉它结束响应会起作用。 Not the most graceful way to handle this but it does in fact work. 处理这个问题并不是最优雅的方式,但实际上确实有效。

So 所以

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

instead of 代替

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

So you'd have this in your controller: 所以你在你的控制器中有这个:

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

Perhaps you get a blank page when you run from Visual Studio under development server using Windows authentication ( previous topic ). 当您使用Windows身份验证( 上一主题 )从开发服务器下的Visual Studio运行时,可能会得到一个空白页面。

If you deploy to IIS you can configure custom error pages for specific status codes, in this case 401. Add httpErrors under system.webServer: 如果部署到IIS,则可以为特定状态代码配置自定义错误页面,在本例中为401.在system.webServer下添加httpErrors:

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

Then create ErrorController.Unauthorized method and corresponding custom view. 然后创建ErrorController.Unauthorized方法和相应的自定义视图。

You should build your own Authorize-filter attribute. 您应该构建自己的Authorize-filter属性。

Here's mine to study ;) 这是我的学习;)

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

In your Startup.Auth.cs file add this line: 在Startup.Auth.cs文件中添加以下行:

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

Example: 例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM