最近在用MVC实现一些权限管理方面的东西,大概思路是继承AuthorizeAttribute写了一个子类UserAuthorizeAttribute,在需要验证的Action或controller中引用该Attribute。重写了三个方法,依执行顺序分别是OnAuthorization -> AuthorizeCore -> HandleUnauthorizedRequest。
在OnAutorization中获取特性标记的方法的区域名、控制器名、操作方法名。在AuthorizeCore中判断用户是否已经登陆,若没登陆直接返回false,如果登陆则判断该用户对应的areaName、controllerName、actionName是否有访问权限(记录在数据库),有则返回true,否则设置Response.StatusCode=403,并返回false。
在HandleUnauthorizedRequest中判断如果Response.StatusCode==403则提示用户没该权限,否则调用base.HandleUnauthorizedRequest跳转到登陆页。
public override void OnAuthorization(AuthorizationContext filterContext)
{
actionName = filterContext.ActionDescriptor.ActionName;
controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
areaName = filterContext.RouteData.DataTokens["area"] as string;
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("HttpContext");
if (!httpContext.User.Identity.IsAuthenticated) return false;//没有登陆
ActionRoles = ServiceFactory.SystemModuleService.GetActionRoles(actionName, controllerName, areaName);
UserRoles = HttpContextHelper.GetCurrentUserRoles();//获取当前用户角色
if (ActionRoles != null && UserRoles != null &&
UserRoles.Any(x => ActionRoles.Contains(x))) return true;
httpContext.Response.StatusCode = 403;
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException("filterContext");
//if(filterContext.HttpContext.Request.IsAjaxRequest())
switch (filterContext.HttpContext.Response.StatusCode)
{
case 403:
filterContext.Result = new RedirectResult("~/Error/Forbidden");
break;
default:
base.HandleUnauthorizedRequest(filterContext);
break;
}
}
其中在HandleUnauthorizedRequest中直接跳转到"~/Error/Forbidden",总给我一种很莫名的感觉,理想中是出现下图这样的效果,然后由web.config中指定的customError去处理:
尝试过用设置filterContext.Result=new HttpUnauthorizedResult()或者filterContext.Result=new HttpStatusCodeResult(..),HttpStatusCodeResult都不行,返回的都是空白页面。
后在stackoverflow中找到一篇这样的文章:How to return a view for HttpNotFound() in ASP.Net MVC 3? 才得以解决,其实就是一句话:
case 403:
throw new HttpException(403, "Forbidden");//直接抛出403,由customErrors决定如何处理.
写到这里,惭愧,那个惭愧啊~~~
顺便贴下代码,用烂笔头记下customErrors的设置及具体实现:
<customErrors mode="On" >
<error statusCode="400" redirect="~/Error/BadRequest" />
<error statusCode="403" redirect="~/Error/Forbidden" />
<error statusCode="404" redirect="~/Error/NotFound" />
<error statusCode="408" redirect="~/Error/Timeout" />
<error statusCode="500" redirect="~/Error/InternalServerError" />
<error statusCode="503" redirect="~/Error/ServiceUnavailable" />
</customErrors>
我没有设置defaultRedirect是我认为全局的HandleErrorAttribute会引用Share下的Error.cshtml帮我处理。我用一个ErrorController来实现以上错误的处理:
/// <summary>
/// 403
/// </summary>
public virtual ActionResult Forbidden()
{
ViewBag.ExceptionIco = "icon-error-forbidden";
ViewBag.ErrorDescription = "您所处的用户组没有权限访问该功能。";
return View("Index");
}
/// <summary>
/// 404
/// </summary>
public virtual ActionResult NotFound()
{
ViewBag.ExceptionIco = "icon-error-notfound";
ViewBag.ErrorDescription = "您请求的页面不存在。";
return View("Index");
}
.....