自定义Authorize attribute:WebAPI返回带JSON的401/403及MVC重定向
嘿,我之前在做混合MVC+Web API的项目时,刚好碰到过完全一样的需求,给你分享下我落地的方案,一步步搞定你的三个问题:
1. 先搞定核心:区分Web API和MVC请求
判断请求类型的方式有几种,我觉得最靠谱的是通过ActionDescriptor的类型来区分,它直接对应了当前请求的处理类型:
private bool IsApiRequest(AuthorizationContext filterContext) { // Web API的ActionDescriptor是ReflectedHttpActionDescriptor return filterContext.ActionDescriptor is System.Web.Http.Controllers.ReflectedHttpActionDescriptor; }
如果是ASP.NET Core项目,也可以通过请求路径前缀(比如/api)或者判断控制器是否继承自ControllerBase来区分,但老框架用上面的方法更准确。
2. Web API返回带JSON payload的401/403
在自定义AuthorizeAttribute里重写HandleUnauthorizedRequest方法,判断是API请求后,直接构造自定义JSON结果并设置对应状态码:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (IsApiRequest(filterContext)) { // 根据授权逻辑判断返回401还是403 var statusCode = filterContext.HttpContext.User.Identity.IsAuthenticated ? 403 : 401; var errorMsg = statusCode == 401 ? "请先登录以访问该接口" : "您没有足够权限访问该资源"; // 构造自定义响应体 var errorResponse = new { StatusCode = statusCode, Message = errorMsg, Timestamp = DateTime.UtcNow, Details = "如需帮助,请联系管理员" }; // 返回JSON结果并设置状态码 filterContext.Result = new JsonResult { Data = errorResponse, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; filterContext.HttpContext.Response.StatusCode = statusCode; return; } // 下文处理MVC请求逻辑 }
3. MVC Action返回401/403并重定向指定URL
对于MVC请求,我们需要返回重定向结果,同时手动设置正确的状态码(默认重定向是302,需要改成401/403):
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (IsApiRequest(filterContext)) { // 上面的API处理逻辑 } else { // 可配置跳转URL,让属性更灵活 string loginUrl = "~/Account/Login"; string accessDeniedUrl = "~/Account/AccessDenied"; if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { // 未认证(401)跳登录页 filterContext.Result = new RedirectResult(loginUrl); filterContext.HttpContext.Response.StatusCode = 401; } else { // 无权限(403)跳无权限提示页 filterContext.Result = new RedirectResult(accessDeniedUrl); filterContext.HttpContext.Response.StatusCode = 403; } } }
如果想让跳转URL更灵活,可以给自定义Attribute加可配置属性:
public class CustomAuthorizeAttribute : AuthorizeAttribute { public string LoginUrl { get; set; } = "~/Account/Login"; public string AccessDeniedUrl { get; set; } = "~/Account/AccessDenied"; // 使用时直接配置:[CustomAuthorize(LoginUrl = "~/Auth/Login")] }
完整的自定义AuthorizeAttribute示例
把所有逻辑整合起来,完整代码如下:
using System; using System.Web.Mvc; using System.Web.Http.Controllers; using System.Security.Principal; public class CustomAuthorizeAttribute : AuthorizeAttribute { public string LoginUrl { get; set; } = "~/Account/Login"; public string AccessDeniedUrl { get; set; } = "~/Account/AccessDenied"; // 核心授权逻辑:判断用户是否登录、是否有权限 protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) { if (!httpContext.User.Identity.IsAuthenticated) return false; // 替换成你的实际权限检查逻辑,比如检查角色/自定义权限 return CheckUserHasPermission(httpContext.User); } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (IsApiRequest(filterContext)) { HandleApiUnauthorized(filterContext); } else { HandleMvcUnauthorized(filterContext); } } // 处理API授权失败 private void HandleApiUnauthorized(AuthorizationContext filterContext) { var statusCode = filterContext.HttpContext.User.Identity.IsAuthenticated ? 403 : 401; var errorMsg = statusCode == 401 ? "请先登录以访问该接口" : "您没有足够权限访问该资源"; var errorResponse = new { StatusCode = statusCode, Message = errorMsg, Timestamp = DateTime.UtcNow }; filterContext.Result = new JsonResult { Data = errorResponse, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; filterContext.HttpContext.Response.StatusCode = statusCode; } // 处理MVC授权失败 private void HandleMvcUnauthorized(AuthorizationContext filterContext) { if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.Result = new RedirectResult(LoginUrl); filterContext.HttpContext.Response.StatusCode = 401; } else { filterContext.Result = new RedirectResult(AccessDeniedUrl); filterContext.HttpContext.Response.StatusCode = 403; } } // 判断是否为API请求 private bool IsApiRequest(AuthorizationContext filterContext) { return filterContext.ActionDescriptor is ReflectedHttpActionDescriptor; } // 自定义权限检查逻辑 private bool CheckUserHasPermission(IPrincipal user) { // 示例:检查用户是否为管理员角色 // return user.IsInRole("Admin"); return false; // 替换成你的实际逻辑 } }
内容的提问来源于stack exchange,提问作者iraSenthil
相关产品推荐
相关产品推荐

