在MVC中设置403错误页面

17

我重写了类以执行自定义授权

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult(403);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

现在我已经在 web.config 文件中配置了 403 错误页面。

<customErrors defaultRedirect="/Shared/Error" mode="On">
  <error statusCode="403" redirect="/Shared/UnAuthorize" />
</customErrors>

但浏览器仍然向我显示403的默认错误页面,我错过了什么,有任何想法吗?

7个回答

12

我知道这是一个很老的问题,但我发布这篇文章是为了那些可能遇到相同问题的人。就像我一样,我曾经也遇到过同样的问题并将其解决了。如果您想触发web.config中的customErrors元素,则可以尝试以下方法。

protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
    throw new HttpException(403, "Forbidden");
}

完美地运作,比其他解决方案更加简便。 - Guillaume
3
你把这个放在哪里?如果可以的话最好加上。它可以是区域特定的吗? - CularBytes
@RageCompex 你可以将它们放在你的自定义属性类中。你能详细解释一下“区域特定”是什么意思吗? - genki98
我的建议是使用 System.Net.HttpStatusCode 枚举来获取状态码。例如:(int)HttpStatusCode.Forbidden。 - Renan Araújo

11

除了Max B.的答案之外,还有一个小提示/备注:

当我使用自定义错误时,我会创建一个ErrorsController,并创建一个未经授权的ActionResult,然后执行以下操作:

<error statusCode="403" redirect="/Errors/UnAuthorize" />

通过这种方式,我可以在控制器中添加额外的信息或执行其他操作,例如:

  • 记录尝试访问经过身份验证的区域的人员信息并将其记录到数据库中。
  • 错误计数。
  • 可能还有一个漏洞或报告表单,用户可以使用它发送管理员信息。
  • ...

这样,您就可以对发生的情况有更多的控制。


实际上我正在做同样的事情,Shared是控制器,Unauthorized是操作,但我仍然收到相同的默认HTTP 403页面错误,而不是我的定义页面。 - Saboor Awan
1
非常有用的链接 https://dev59.com/7nE95IYBdhLWcg3wAo9U - Saboor Awan
2
@SaboorAwan。我尝试了相同的方法,但没有成功。控制器方法从未被调用。你是通过这个答案还是你发布的链接解决了这个问题? - Marco

6

当我编写自己的自定义AuthorizeAttribute时,我遇到了与您完全相同的问题。当我在web.config中添加“customErrors”标签时,403的自定义错误页面不会显示出来。 这是我解决它的方法:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
           filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new
                        { 
                            controller = "Error", 
                            action = "Unauthorised" 
                        })
                ); 

        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

我想要显示一个路由来过滤filterContext.Result,而不是指定403 HttpStatusCode。


3
您可以尝试这种替代方案,而不是使用“:”:
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult(403);

你可以将它更改为:
if (filterContext.HttpContext.Request.IsAuthenticated)
        {               
            throw new UnauthorizedAccessException();
        }

在您的控制器 / BaseController 中重写 OnException(ExceptionContext filterContext) 方法。
protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }

        if (filterContext.Exception.GetType() == typeof(UnauthorizedAccessException))
        {   
            filterContext.Result = new ViewResult
            {
                ViewName = "~/Views/Error/NotAuthorized.cshtml"
            };
            filterContext.ExceptionHandled = true;
            return;
        }

        base.OnException(filterContext);
    }

0
在我看来,HttpStatusCodeResult(403)位于错误的if分支中。 我认为代码应该像这样:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(filterContext);
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult(403);
        }
    }
}

2
你可能想要测试一下...当已认证时,它是否绕过了授权要求。 - JustinStolle

0

如何在MVC中处理401(未授权),403(禁止)和500(内部服务器错误)。 适用于ajax /非ajax 调用并在aspx表单身份验证下。

它可以更改以不同方式处理各种未捕获的异常,并根据请求是否为ajax而做出不同反应。 验证部分允许它绕过任何常规mvc web表单重定向到登录页,而是返回401未经授权-然后您的客户端js框架可以更轻松地对http状态401/403作出反应。

// FilterConfig.cs:
filters.Add(new ApplicationAuthorizeAttribute());
filters.Add(new ApplicationHandleErrorAttribute());

public class ApplicationAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Note: To reach here, a Web.config path-specific rule 'allow users="?"' is needed (otherwise it redirects to login)

        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.IsAjaxRequest())
        {
            response.SuppressFormsAuthenticationRedirect = true;
            response.TrySkipIisCustomErrors = true;
        }

        filterContext.Result = new HttpUnauthorizedResult();
    }
}

public class ApplicationHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var exception = context.Exception is AggregateException
            ? ((AggregateException)context.Exception).InnerExceptions.First()
            : context.Exception;
        var request = context.HttpContext.Request;
        var response = context.HttpContext.Response;
        var isAjax = request.IsAjaxRequest();

        if (exception is MyCustomPermissionDeniedException)
        {
            filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
            response.TrySkipIisCustomErrors = isAjax;
            filterContext.ExceptionHandled = true;
            return;
        }

#if DEBUG
        if (!isAjax)
        {
            // Show default aspx yellow error page for developers
            return;
        }
#endif

        var requestUri = request.Url == null ? "" : request.Url.AbsoluteUri;
        MyCustomerLogger.Log(exception, requestUri);

        response.Clear();
        response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;

#if DEBUG
        var errorMessage = exception.Message;
#else
        var errorMessage = "An error occurred, please try again or contact the administrator.";
#endif

        response.Write(isAjax
            ? JsonConvert.SerializeObject(new {Message = errorMessage})
            : errorMessage);
        response.End();
        response.TrySkipIisCustomErrors = true;
        context.ExceptionHandled = true;
    }
}

Web.config:

<system.webServer>

<authentication mode="Forms">
  <forms name=".MYAUTHCOOKIE" protection="All" loginUrl="/Account/Login" timeout="18000" slidingExpiration="true" enableCrossAppRedirects="false" />
</authentication>

<authorization>
  <deny users="?" />
</authorization>

</system.webServer>

<!-- ajax api security done via ApplicationAuthorizeAttribute -->
<location path="api">
  <system.web>
    <authorization>
      <allow users="?"/>
    </authorization>
  </system.web>
</location>

Web服务API请求的附加路由:(放在常规MVC路由之上)

// This route has special ajax authentication handling (no redirect to login page)
routes.MapRoute(
    name: "DefaultApi",
    url: "api/{controller}/{action}/{id}",
    defaults: new { id = UrlParameter.Optional }
);

使用 jQuery 处理错误的示例客户端代码:

$.ajaxSetup({
    complete: function onRequestCompleted(xhr, textStatus) {
        if (xhr.readyState == 4 && xhr.status == 401) {
            // Not needed with smart status: && xhr.responseText.substring(0, 150).indexOf("<title>Log in") != -1
            //location.href = "/Account/Login";
            alert("Your session has timed out.");
        }
    }
});

或者,您可以通过ApplicationHandleErrorAttribute处理所有的身份验证,并且摆脱web.config中的deny users="?". 但是我有一个旧的aspx页面,它不会触发mvc过滤器,所以我想保留那个deny users="?".


你应该能够使用F12(resharper反编译?ms符号服务器?)进入以下内容:ControllerActionInvoker,HandleErrorAttribute,AuthorizeAttribute。这是非常有启发性的阅读材料。ControllerActionInvoker以不同的方式使用每个4种过滤器类型。(auth,action,result,exception)例如,转到您的控制器,在基类“Controller”上按F12,在“IAuthorizationFilter”上按F12,在“OnAuthorization”上按Shift-F12。 - Curtis Yallop

0

1-创建一个名为LoggedOrAuthorizedAttribute的类

    public class LoggedOrAuthorizedAttribute: AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            CheckIfUserIsAuthenticated(filterContext);
        }

        private void CheckIfUserIsAuthenticated(AuthorizationContext filterContext)
        {
            // If Result is null, we're OK: the user is authenticated and authorized. 
            if (filterContext.Result == null)
                return;

            // If here, you're getting an HTTP 401 status code. In particular,
            // filterContext.Result is of HttpUnauthorizedResult type. Check Ajax here. 
            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                filterContext.Result = new RedirectResult("/Error/Error401");
            }
        }
     }

2-将您创建的属性添加到操作顶部

    [LoggedOrAuthorizedAttribute(Roles = "Admin")]
    public ActionResult Index()
    {
        return View();
    }

    [LoggedOrAuthorizedAttribute(Roles = "User")]
    public ActionResult IndexUser()
    {
        return View();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接