JWTBearerHandler 令牌失败但请求仍在处理中

JWT BearerHandler Token Falied but request is still processed

我 运行 遇到了一个非常奇怪的问题,我猜我在我的设置中遗漏了一些东西。

我有一个由 IdentityServer4 保护的 WebAPI。它仅使用 Client_credentials。如果我写错了 ClientId och ClientSecret 用户未通过身份验证,我将无法连接到我的 WebAPI。但是,如果我写错了范围名称,请求仍会得到处理,并且我会收到响应,奇怪的是会抛出异常,但由于某种原因,它会被 .NET Core Framework 忽略。

这是我输出的一些调试信息 window。

Microsoft.AspNetCore.Hosting.Internal.WebHost:信息:请求开始 HTTP/1.1 GET https://localhost:44360/v1/bookings

    Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'.
           at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
           at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
           at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
           at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
           at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
        Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'.
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'TRS.BookingService.Api.Controllers.BookingsController.Get (TRS.BookingService.Api)'
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Route matched with {action = "Get", controller = "Bookings"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.ActionResult`1[System.Collections.Generic.IEnumerable`1[System.String]]] Get() on controller TRS.BookingService.Api.Controllers.BookingsController (TRS.BookingService.Api).
        Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method TRS.BookingService.Api.Controllers.BookingsController.Get (TRS.BookingService.Api) - Validation state: Valid
        TRS.BookingService.Api.Controllers.BookingsController:Information: Getting all bookings
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action method TRS.BookingService.Api.Controllers.BookingsController.Get (TRS.BookingService.Api), returned result Microsoft.AspNetCore.Mvc.ObjectResult in 96.2159ms.
Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'System.String[]'.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action TRS.BookingService.Api.Controllers.BookingsController.Get (TRS.BookingService.Api) in 280.2344ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'TRS.BookingService.Api.Controllers.BookingsController.Get (TRS.BookingService.Api)'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 1345.3829ms 200 application/json; charset=utf-8

因此,即使抛出异常表明令牌未验证,仍然允许请求继续并执行并将响应发送回客户端。

这是 ConfigureServices 的样子:

    services
        .AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.Authority = "https://localhost:44392/";
            options.Audience = "FAKE_SCOPE";
        });

以及 Configure() 方法

    app.UseAuthentication();
    app.UseMvc();

这是 JWT 令牌的样子:

{
  "nbf": 1562062882,
  "exp": 1562066482,
  "iss": "https://localhost:44392",
  "aud": [
    "https://localhost:44392/resources",
    "bookingApi"
  ],
  "client_id": "clientId",
  "scope": [
    "bookingApi"
  ]
}

这是调用 API 的客户端代码。

        var idpUrl = "https://localhost:44392/";    
        var clientId = "clientId";
        var clientSecret = "secret";
        var scope = "bookingApi";

        var accessToken = await GetAccessTokenAsync(new Uri(idpUrl), clientId, clientSecret, scope);
        string content = await GetContent(new Uri("https://localhost:44360/v1/bookings"), accessToken);

我想我在授权方面遗漏了一些东西,我尝试了不同的方法

services.Authorization()

在 ConfigureServices() 方法中,但它没有帮助,我猜我写错了。

最好的问候 马格努斯

在花了一天时间弄清楚为什么它不起作用后,我决定 step-thru Microsoft 代码并在 AuthenticationMiddleware 中找到它。

public class 身份验证中间件 { 私人只读 RequestDelegate _next;

public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
{
    if (next == null)
    {
        throw new ArgumentNullException(nameof(next));
    }
    if (schemes == null)
    {
        throw new ArgumentNullException(nameof(schemes));
    }

    _next = next;
    Schemes = schemes;
}

public IAuthenticationSchemeProvider Schemes { get; set; }

public async Task Invoke(HttpContext context)
{
    context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
    {
        OriginalPath = context.Request.Path,
        OriginalPathBase = context.Request.PathBase
    });

    // Give any IAuthenticationRequestHandler schemes a chance to handle the request
    var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
    foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
    {
        var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
        if (handler != null && await handler.HandleRequestAsync())
        {
            return;
        }
    }

    var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
    if (defaultAuthenticate != null)
    {
        var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
        if (result?.Principal != null)
        {
            context.User = result.Principal;
        }
    }

    await _next(context);
}

}

基本上发生的事情是结果上有一个失败属性,其中包含我的身份验证异常,但由于代码中没有检查它,它将继续请求管道中的下一个中间件.所以我基本上写了自己的 AuthenticationMiddleware 添加检查 Failure 是否有值然后 return 403.

        var defaultAuthenticate = await _schemas.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
            if (result?.Principal != null)
                context.User = result.Principal;

            if (result?.Failure != null)
                throw new AuthorizationException(result.Failure.Message);
        }

        await _next(context);
    }
    catch (AuthorizationException ex) when (!context.Response.HasStarted)
    {
        _logger.LogWarning(ex, "Unauthorized access encountered.");

        context.Response.Clear();
        context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
    }

然而,除了我之外,这不是我要做的事情,所以如果有人知道我为什么需要这样做,我会很高兴提供信息。

我在遇到同样的问题后看到了这篇文章。在多次敲击头部之后,我发现在我的案例中,这是通过使用 services.AddMvcCore()(在我的案例中使用 .AddJsonFormatters().AddDataAnnotations())而不是 services.AddMvc() 引起的。只有使用 .AddMvcCore() 我才能得到 401 令牌验证失败。

似乎您需要在使用 .AddMvcCore 时将 .AddAuthorization() 添加到组合中,因为默认情况下不添加它。没有它,令牌验证失败,但请求管道继续很愉快。