ASP 核心 API - 自定义未授权主体

ASP Core API - Custom Unauthorized body

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

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

如果用户未登录(在尝试访问标有 [Authorize] 属性的操作时)或用户的令牌未通过身份验证,我如何创建自己的响应。

我遇到了一个使用从默认属性继承的自定义授权属性的解决方案。在这个例子中, HandleUnauthorizedRequest 方法被覆盖了。但是我在 AuthorizeAttribute class.

中没有看到这样的方法

有没有办法使用 http 正文创建自定义 unauthorized 响应?

由于您使用的是 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 中的默认质询逻辑,您可以找到 here 以供参考。

默认逻辑不写任何内容到response(它只设置状态码和设置一些headers)。因此,要继续使用默认逻辑并在其上添加内容,您可以使用如下内容:

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;
    }
};

对于.net core 5 web api project with jwt authentication 在[=22的Configure方法中使用这个中间件=] 在 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);
    }
}

这就是我想出的响应方式,与在 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);
        }
    };
});