ASP Core API - 自定义未授权响应体

6

我正在使用dotnet core v3.1开发ASP Core Web API。

我正在使用JWT token进行身份验证。而对于授权,我使用 [Authorize]属性。

如何在用户未登录(尝试访问标记有 [Authorize]属性的操作)或用户的token未经身份验证时创建自己的响应。

我发现了一种解决方案,即使用从默认属性继承的自定义授权属性。在这个例子中,HandleUnauthorizedRequest方法被覆盖。但是我没有在AuthorizeAttribute类中看到这样的方法。

是否有一种方法可以创建带有HTTP正文的自定义 unauthorized 响应?

3个回答

18

如果您正在使用JWT承载身份验证,覆盖默认的验证逻辑(处理401未经授权的问题)的一种方法是在Startup.ConfigureServices中挂接一个处理程序到JwtBearerEvents.OnChallenge回调:

services.AddAuthentication().AddJwtBearer(options =>
{
    // Other configs...
    options.Events = new JwtBearerEvents
    {
        OnChallenge = async context =>
        {
            // Call this to skip the default logic and avoid using the default response
            context.HandleResponse();

            // Write to the response in any way you wish
            context.Response.StatusCode = 401;
            context.Response.Headers.Append("my-custom-header", "custom-value");
            await context.Response.WriteAsync("You are not authorized! (or some other custom message)");
        }
    };
});

这将覆盖 JwtBearerHandler.HandleChallengeAsync 中的默认挑战逻辑,您可以在此处参考

默认逻辑不向响应中写入任何内容(仅设置状态码和一些标头)。因此,为了继续使用默认逻辑并在其之上添加内容,您可以使用类似以下代码:

options.Events = new JwtBearerEvents
{
    OnChallenge = context =>
    {
        context.Response.OnStarting(async () =>
        {
            // Write to the response in any way you wish
            await context.Response.WriteAsync("You are not authorized! (or some other custom message)");
        });

        return Task.CompletedTask;
    }
};

有没有一种方法可以获取默认实现生成的响应并将HTTP正文添加到其中? 如果身份验证失败,我只想让响应具有JSON正文:{success: false, errorMessage: "Not authorized"}。 - satma0745
2
@catman0745 默认实现不会写入任何内容,它只设置响应代码和一些头信息。请参见编辑后的答案。 - scharnyw
谢谢!那真的很有帮助。 - Victor Gazzinelli
无法工作! 我得到: 响应头 内容长度:0 日期:2021年8月19日星期四12:58:51 GMT 服务器:Kestrel WWW-Authenticate:Bearer - M Komaei
有没有办法在单个控制器上完成,而不是整个应用程序? - Dunge

5

这是我为响应与在ApiController中返回Unauthorized()相同的ProblemDetails所想出的方法:

.AddJwtBearer(options =>
{
    // Other configs...
    options.Events = new JwtBearerEvents
    {
        OnChallenge = async context =>
        {
            // Call this to skip the default logic and avoid using the default response
            context.HandleResponse();

            var httpContext = context.HttpContext;
            var statusCode = StatusCodes.Status401Unauthorized;

            var routeData = httpContext.GetRouteData();
            var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());

            var factory = httpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
            var problemDetails = factory.CreateProblemDetails(httpContext, statusCode);

            var result = new ObjectResult(problemDetails) { StatusCode = statusCode };
            await result.ExecuteResultAsync(actionContext);
        }
    };
});

1
这是一个非常灵活的解决方案,可以响应任何自定义的ResultObject,比如带有附加信息的JsonResult,我很喜欢它。 - Mustafa Salih ASLIM
请帮忙查看以下链接:https://dev59.com/HNP7oIgBc1ULPQZF2t1-。 - Ajt

5
在Startup.cs的Configure方法中使用此中间件,适用于带有JWT身份验证的.NET Core 5 Web API项目,以在Swagger中显示ErrorDto:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoginService v1"));
    }

    app.ConfigureExceptionHandler();
    app.UseHttpsRedirection();
    app.UseRouting();
        
    // Unauthorized (401) MiddleWare
    app.Use(async (context, next) =>
    {
        await next();
     
        if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized) // 401
        {
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(new ErrorDto()
            {
                StatusCode = 401,
                Message = "Token is not valid"
            }.ToString());
        }
    });

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

ErrorDto :

public class ErrorDto
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    
    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接