如何启用自定义授权消息

时间:2020-06-04 16:21:16

标签: c# asp.net-core jwt asp.net-core-identity

免责声明:此主题的结果通常很旧,并且不一定引用使用Identity Core的Asp.Net Core 3.1。我正在尝试找到一种现代的推荐解决方案。

我已经使用Identity和JWT创建了概念证明Web API应用程序。这个想法是导航到网站并能够登录并查看您的声明,更改您的密码以及Identity附带的其他功能。

另一个组件是仅将Web API与JWT一起使用。

我已经执行了声明/政策,并且授权运行良好。总的来说,我对这种概念证明感到满意。

尤其是有一件事情尚待改进:当通过JWT使用API​​时,未认证用户或未认证用户时的消息。目前,它所做的只是返回没有相关消息的403(已认证但未授权)或401(未认证)响应代码。

以这个例子为例。我有一个声明,ClaimType: Api, ClaimValue: Delete映射到一个策略(字符串常量以避免魔术字符串):CanDeletePolicy

options.AddPolicy(Policies.CanDeletePolicy, policy => {
    policy.RequireClaim("Api", "Delete");
});

请记住,一切都按预期进行。我只是想为API使用者提供有意义的消息。

控制器:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
[Produces("application/json")]

public class PeopleController : ControllerBase {
    private readonly ILogger<PeopleController> _logger;
    private readonly IPeopleService _peopleService;

    public PeopleController(ILogger<PeopleController> logger, IPeopleService peopleService) {
        _logger = logger;
        _peopleService = peopleService;
    }

    ...

PeopleController中的操作:

[HttpDelete("[action]/{id:int}")]
[Authorize(Policy = Policies.CanDeletePolicy)]
public IActionResult Delete(int id) {
    var result = _peopleService.Delete(id);

    var username = User.FindFirstValue(ClaimTypes.Name);

    if (result < 1) {
        var message = $"User '{username}' was unable to delete a person with Id {id}";
        _logger.LogError(message);

        return BadRequest(new {
            Message = message
        });
    }

    var successMessage = $"User '{username}' successfully deleted the person with Id {id}";
    _logger.LogDebug(successMessage);

    return Ok(new {
        Message = "Person deleted"
    });
}

如果用户尝试使用此操作:

  • 但未通过身份验证,他们收到一个空的401响应
  • 已通过身份验证,但未获得授权,他们会收到一个空的403响应

我想提供不同程度的信息:

  • 401,其响应正文为“您未通过身份验证”

  • 403,其响应正文为“您未被授权”或“您未被授权:Api,需要更新”

我有什么选择?创建自定义的Authorize属性?我不想只是为了添加一条简单的信息而重新发明轮子。我也希望以JSON格式进行授权响应,但我认为这对于拟议的解决方案并不重要。

1 个答案:

答案 0 :(得分:1)

   public class ResponseFormatterMiddleware
{
    private readonly RequestDelegate _next;

    public ResponseFormatterMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        await _next.Invoke(context);

        if (context.Response.StatusCode == StatusCodes.Status401Unauthorized)
        {
            await context.Response.WriteAsync(
                JsonConvert.SerializeObject(new ResponseModel("some-message")));
        }
    }
}

public class ResponseModel
{
    public ResponseModel(string message)
     {
        this.Message = message;
     }

     public string Message { get; set; }
}

在启动时:

      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // put middleware before authentication
        app.UseMiddleware<ResponseFormatterMiddleware>();
        app.UseAuthentication();
    }