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

ASP.NET Core 控制器/动作级过滤器未授权时返回JSON响应的实现方案

解决方案:ASP.NET Core 混合认证下控制器级自定义未授权JSON响应

实现思路

你之前尝试的全局Cookie事件、Handler重写逻辑并不是真的只能全局生效,只需要在逻辑中增加端点元数据判断,就可以仅针对标记了指定特性的控制器/动作走自定义JSON响应逻辑,其余场景保持原有跳转/原生401逻辑不变,不需要新增混合认证方案,也不需要修改现有认证校验逻辑。


具体实现步骤

1. 定义标记特性

先定义一个空的特性类,用于标记需要返回自定义未授权JSON的控制器或动作,同时支持自定义错误信息:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class ReturnUnauthorizedJsonAttribute : Attribute
{
    /// <summary>
    /// 自定义错误提示
    /// </summary>
    public string Message { get; set; } = "身份认证失败,请提供有效的认证信息";
    /// <summary>
    /// 自定义业务错误码
    /// </summary>
    public int ErrorCode { get; set; } = 40100;
}

2. 修改Cookie认证事件分支逻辑

修改原有AddCookie的配置,在跳转事件中增加端点特性判断,有标记的返回JSON,无标记的走原有跳转逻辑:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        // 原有配置保留不变
        options.Cookie.Name = "_auth";
        options.Cookie.HttpOnly = true;
        options.LoginPath = new PathString("/Account/Login");
        options.LogoutPath = new PathString("/Account/LogOff");
        options.AccessDeniedPath = new PathString("/Account/Login");
        options.ExpireTimeSpan = TimeSpan.FromHours(4);
        options.SlidingExpiration = true;

        // 新增跳转逻辑分支判断
        options.Events.OnRedirectToLogin = context =>
        {
            // 获取当前请求的端点元数据
            var endpoint = context.HttpContext.GetEndpoint();
            var jsonAttr = endpoint?.Metadata.GetMetadata<ReturnUnauthorizedJsonAttribute>();
            if (jsonAttr != null)
            {
                // 有标记则返回JSON
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                context.Response.ContentType = "application/json;charset=utf-8";
                return context.Response.WriteAsJsonAsync(new
                {
                    code = jsonAttr.ErrorCode,
                    msg = jsonAttr.Message
                });
            }
            // 无标记走原有跳转逻辑
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };

        options.Events.OnRedirectToAccessDenied = context =>
        {
            var endpoint = context.HttpContext.GetEndpoint();
            var jsonAttr = endpoint?.Metadata.GetMetadata<ReturnUnauthorizedJsonAttribute>();
            if (jsonAttr != null)
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                context.Response.ContentType = "application/json;charset=utf-8";
                return context.Response.WriteAsJsonAsync(new
                {
                    code = 40300,
                    msg = "权限不足,无法访问当前资源"
                });
            }
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };
    })
    .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

3. 修改Basic认证Handler的挑战逻辑

在你自定义的BasicAuthenticationHandler中重写HandleChallengeAsync方法,同样增加端点特性判断:

protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
    var endpoint = Context.GetEndpoint();
    var jsonAttr = endpoint?.Metadata.GetMetadata<ReturnUnauthorizedJsonAttribute>();
    if (jsonAttr != null)
    {
        Response.StatusCode = StatusCodes.Status401Unauthorized;
        Response.ContentType = "application/json;charset=utf-8";
        return Response.WriteAsJsonAsync(new
        {
            code = jsonAttr.ErrorCode,
            msg = jsonAttr.Message
        });
    }
    // 无标记走原有Basic认证挑战逻辑,返回WWW-Authenticate响应头
    Response.Headers.WWWAuthenticate = "Basic realm=\"你的站点标识\"";
    return base.HandleChallengeAsync(properties);
}

使用方法

直接在需要支持双认证+自定义JSON响应的控制器/动作上叠加标记即可,动作上的标记会覆盖类上的全局配置:

// 控制器级别标记,所有动作生效
[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "BasicAuthentication,Cookies")]
[ReturnUnauthorizedJson(Message = "API接口需要身份认证", ErrorCode = 40101)]
public class OrderApiController : ControllerBase
{
    [HttpGet("list")]
    public IActionResult GetList()
    {
        return Ok();
    }

    // 动作级别标记,覆盖类的配置
    [HttpPost("create")]
    [ReturnUnauthorizedJson(Message = "创建订单需要管理员权限", ErrorCode = 40102)]
    public IActionResult CreateOrder()
    {
        return Ok();
    }
}

方案优势

  • 完全不影响原有页面的Cookie跳转逻辑、原生Basic认证逻辑
  • 控制器/动作粒度的控制,支持自定义错误信息和业务错误码
  • 不需要新增混合认证方案,原有认证校验逻辑无需修改
  • 性能损耗极低,仅多一次特性判断开销

内容的提问来源于stack exchange,提问作者Dunge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 16:39:05