You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Authorize attribute:WebAPI返回带JSON的401/403及MVC重定向

自定义AuthorizeAttribute解决API和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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:55:47