簡體   English   中英

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

[英]Redirecting unauthorized controller in ASP.NET MVC

我在ASP.NET MVC中有一個控制器,我已將其限制為管理員角色:

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

如果不在Admin角色中的用戶導航到此控制器,則會顯示空白屏幕。

我想要做的是將它們重定向到View,其中顯示“您需要處於Admin角色才能訪問此資源”。

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

基於AuthorizeAttribute創建自定義授權屬性並覆蓋OnAuthorization以執行檢查完成的方式。 通常,如果授權檢查失敗,AuthorizeAttribute會將過濾結果設置為HttpUnauthorizedResult。 您可以將其設置為ViewResult(Error視圖)。

編輯 :我有一些博客文章更詳細:

例:

    [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身份驗證,_isAuthorized只是HttpContext.User.Identity.IsAuthenticated ...

我遇到過同樣的問題。 我沒有找出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)

這個問題已經困擾了我好幾天了,所以在找到肯定與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(“/ Login”)不起作用:Action方法仍然被調用,2)FormsAuthentication.RedirectToLoginPage(); 做同樣的事情:Action方法仍然被調用!

這對我來說似乎是完全錯誤的,特別是對於后者:誰會認為FormsAuthentication.RedirectToLoginPage不會結束請求,或者做上面的filterContext.Result = new HttpUnauthorizedResult()的等價物呢?

本來可以留下這個評論,但我需要更多的代表,反正我只想提到Nicholas Peterson,或許可以將第二個參數傳遞給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);
      }
 }

當您使用Windows身份驗證( 上一主題 )從開發服務器下的Visual Studio運行時,可能會得到一個空白頁面。

如果部署到IIS,則可以為特定狀態代碼配置自定義錯誤頁面,在本例中為401.在system.webServer下添加httpErrors:

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

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

您應該構建自己的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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM