如何使授权属性返回自定义的403错误页面,而不是重定向到登录页面
如何使授权属性返回自定义的403错误页面,而不是重定向到登录页面
[Authorize]
属性是一个方便的微软发明,我希望它能解决我现在的问题。
更具体地说:
当当前客户端未经身份验证时,[Authorize]
会将安全操作重定向到登录页面,登录成功后会将用户带回,这很好。
但是,当当前客户端已经通过身份验证但没有权限运行特定操作时,我只需要显示我通用的403页面。
在不将授权逻辑移动到控制器中的情况下,是否可能实现?
更新:
我需要的行为应该在语义上等同于这个草图:
public ActionResult DoWork() { if (!NotAuthorized()) { // 这里不应该是重定向,而是转发 return RedirectToAction("403"); } return View(); }
所以 - 不应该有任何重定向,URL应该保持不变,但页面的内容应该替换为403页面。
更新2:我以这种方式实现了草图:
[HandleError] public class HomeController : Controller { public ActionResult Index() { ViewData["Message"] = "欢迎使用ASP.NET MVC!"; return View(); } [CustomActionFilter] public ActionResult About() { return View(); } public ActionResult Error_403() { return Content("403"); } } public class CustomActionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Result = new ContentResult { Content = "403" }; } }
但我不知道如何正确地将执行转发到HomeController.Action_403(),以便显示403。
更新3:
filterContext.Result = new ViewResult() { ViewName = "Error_403" };
这是如何渲染特定视图模板的答案...但我仍然不知道如何运行另一个控制器 - 无论如何,这已经是一个足够好的解决方案。
问题的原因是默认的AuthorizeAttribute返回的是一个重定向到登录页面的结果,而不是返回一个自定义的403错误页面。解决方法是创建一个自定义的类,继承自AuthorizeAttribute,并重写AuthorizeCore方法,提供自己的授权机制。如果需要更精细的控制授权过程,可以实现IActionFilter接口,并在方法中提供替代行为。这可以通过实现OnActionExecuting方法来实现,在该方法中,可以根据逻辑决定是否调用控制器,以及提供一个替代的ActionResult。
为了返回一个403错误代码,不能使用ContentResult类,需要创建一个自定义的类,继承自ActionResult,并重写ExecuteResult方法,在该方法中设置HttpResponseBase的StatusCode属性为403。可以将Http403Result类进行泛化,接受一个构造函数参数,用于指定要返回的状态码,但概念保持不变。
在回答中还提到了一个问题,即在Web.config中启用customErrors时,是否可以正常工作。根据经验来看,可能不能正常工作,但没有确切的答案。
以下是整理后的文章:
你应该可以创建一个自己的类,该类派生自AuthorizeAttribute,并重写AuthorizeCore方法,以提供你想要的授权机制,这样你就可以使用属性应用你的自定义授权代码,而不是将其移到控制器中。
如果你需要更精细的控制授权,那么我建议你创建一个实现IActionFilter接口的实现(将其作为一个属性应用到你的方法中)。这将允许你在调用控制器之前拦截调用,并在调用控制器方法之前提供替代操作。
这通过在IActionFilter接口上实现OnActionExecuting方法来实现。如果你的逻辑确定根本不应该调用控制器,并且你想提供一个要处理的ActionResult,那么你可以在传入该方法的ActionExecutingContext实例上设置Result属性。通过这样做,该ActionResult将被处理,而不是转到控制器方法获取ActionResult。
如果你想返回一个403错误代码,那么就不能使用ContentResult类。你需要创建一个自己的类,该类派生自ActionResult,并重写ExecuteResult方法,将HttpResponseBase的StatusCode属性设置为403,如下所示:
internal class Http403Result : ActionResult { public override void ExecuteResult(ControllerContext context) { // Set the response code to 403. context.HttpContext.Response.StatusCode = 403; } } public class CustomActionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Result = new Http403Result(); } }
当然,你可以将Http403Result类泛化,以接受一个构造函数参数,用于指定要返回的状态码,但概念是相同的。
在回答中还提到了一个问题,即在Web.config中启用customErrors时,是否可以正常工作。根据经验来看,可能不能正常工作,但没有确切的答案。
问题的原因是,Authorize属性默认情况下会将未授权的用户重定向到登录页面,而不是返回自定义的403错误页面。解决方法是创建一个自定义的Authorize属性子类,重写其HandleUnauthorizedRequest方法,使其在用户已经通过身份验证的情况下返回403状态码。然后,在Web.Config中添加system.webServer\httpErrors部分,将默认的403错误替换为自定义页面。
以下是解决方法的具体代码和配置:
public class MyAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.User.Identity.IsAuthenticated) filterContext.Result = new HttpStatusCodeResult(403); else filterContext.Result = new HttpUnauthorizedResult(); } }
另外,如果想要获取自定义错误403页面/视图中的状态描述,可以使用以下代码:
@{ var statusDescription = Response.StatusDescription; }
以上就是解决问题的方法。