.NET Core 2.2 JWT令牌授权失败,未返回HTTP 401

时间:2019-01-10 21:44:13

标签: authentication asp.net-core jwt http-status-code-401

.NET Core在生产环境中如何处理JSON Web令牌(JWT)身份验证时遇到问题。我正在使用邮递员进行测试。如果我使用有效令牌调用我的API,它将正常工作并返回API端点的预期响应。但是,如果令牌已过期,那么我将无法获得预期的HTTP401。实际上,我没有任何响应,它只是悄无声息地失败了。我的生产配置似乎无法正常工作。如果我在本地开发环境中对此进行测试,则不会有任何问题,并且过期的令牌会收到401未经授权的HTTP响应,如预期的那样。

我的Web应用程序对通过网站登录的用户使用cookie身份验证,对于连接到API的用户使用JWT身份验证。这是JWT身份验证的配置。这在Startup.cs中。这是services.Authentication的JWT配置部分。首先设置cookie身份验证,它是默认的身份验证方案。

.AddJwtBearer(options =>{
options.RequireHttpsMetadata = !_hostingEnvironment.IsDevelopment();

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,

    ValidIssuer = Settings.Api.JwtBearer.TokenValidation.ValidIssuer,
    ValidAudience = Settings.Api.JwtBearer.TokenValidation.ValidAudience,
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Settings.Api.JwtBearer.TokenValidation.IssuerSigningKey))
};

options.Events = new JwtBearerEvents()
{
    OnAuthenticationFailed = context =>
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        context.Response.ContentType = "application/json; charset=utf-8";
        var message = _hostingEnvironment.IsDevelopment() ? context.Exception.ToString() : "An error occurred processing your authentication.";
        var result = JsonConvert.SerializeObject(new { message });
        return context.Response.WriteAsync(result);
    }
};});

这是Startup.cs中的其他相关配置:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error/Exception");

    app.UseStatusCodePagesWithReExecute("/Error/{0}");

    app.UseHsts();

    app.UseHttpsRedirection();
}

app.UseStaticFiles();

app.UseCors(Constants.Security.Cors.PolicyName);

app.UseAuthentication();

app.UseMvc(ConfigureRoutes);}

` 我花了很多时间进行“配置”设置并在生产中进行了测试,但是当令牌认证失败时,我总是最终无响应。由于它在我的本地开发环境中可以正常工作,因此它必须表示我的生产配置有问题,但是我很困惑,没有想法。有关更多信息,下面是错误日志中的错误示例:

  

信息:   Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler [1]         验证令牌失败。 Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException:   IDX10223:生命周期验证失败。令牌已过期。有效:   '[[PII隐藏]]',当前时间:'[[PII隐藏]]'。在   Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime(Nullable 1 notBefore, Nullable 1已过期,SecurityToken securityToken,   TokenValidationParametersvalidationParameters)   System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime(Nullable 1 notBefore, Nullable 1到期,JwtSecurityToken jwtToken,   TokenValidationParametersvalidationParameters)   System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken   jwtToken,TokenValidationParametersvalidationParameters)   System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(字符串   令牌,TokenValidationParametersvalidationParameters,SecurityToken&   validatedToken)   Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.d__6.MoveNext()   信息:   Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler [7]         承载未认证。失败消息:IDX10223:生命周期验证失败。令牌已过期。 ValidTo:'[PII是   隐藏]],目前时间:[[PII隐藏]。信息:   Microsoft.AspNetCore.Authorization.DefaultAuthorizationService [2]         授权失败。信息:Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker [3]         筛选器“ Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter”上的请求授权失败。信息:   Microsoft.AspNetCore.Mvc.ChallengeResult [1]         使用身份验证方案(Bearer)执行ChallengeResult。信息:Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker [2]         在109.4625毫秒内执行了操作Web.Controllers.ApiController.Users(Web)信息:Microsoft.AspNetCore.Routing.EndpointMiddleware [1]         执行的端点'Web.Controllers.ApiController.Users(Web)'失败:Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware [1]         执行请求时发生未处理的异常。 System.InvalidOperationException:无法设置StatusCode,因为   响应已经开始。在   Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32   价值)   Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32   价值)   Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_StatusCode(Int32   价值)   Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.d__7.MoveNext()

预先感谢您提供有关解决此问题的任何想法。

1 个答案:

答案 0 :(得分:1)

因此,我找到了一个局部解决方案,至少可以使我不受阻碍,因此我可以继续进行API开发。尝试更改上下文。JwtBearerEvents OnAuthenticationFailed中的响应在我的生产环境中不起作用(尽管在我的本地计算机上可以正常工作)。我尝试了很多变体,但最后唯一可行的方法是从Startup.cs中删除整个代码块:

options.Events = new JwtBearerEvents()
{
    OnAuthenticationFailed = context =>
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        context.Response.ContentType = "application/json; charset=utf-8";
        var message = _hostingEnvironment.IsDevelopment() ? context.Exception.ToString() : "An error occurred processing your authentication.";
        var result = JsonConvert.SerializeObject(new { message });
        return context.Response.WriteAsync(result);
    }
};

一旦这样做,在Postman中进行测试时,我便开始获得401代码以进行失败的令牌验证,这是一件好事,而且是预期的行为。但是,在响应的正文中,默认错误页面HTML也会被返回。

要解决这个问题,我必须修改Startup.cs中的Configure方法,以便UseStatusCodePagesWithReExecute忽略API:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error/Exception");

        app.UseWhen(context => !context.Request.Path.Value.StartsWith("/api"), builder =>
        {
            builder.UseStatusCodePagesWithReExecute("/Error/{0}");
        });

        app.UseHsts();

        app.UseHttpsRedirection();
    }

    app.UseStaticFiles();

    app.UseCors(Constants.Security.Cors.PolicyName);

    app.UseAuthentication();

    app.UseMvc(ConfigureRoutes);
}

这有效,但我不喜欢它。感觉像一个黑客。现在,我得到了401,用于令牌身份验证失败,体内没有任何东西。就像我之前提到的那样,这使我不受阻碍,但是我仍然想在响应中返回自定义错误消息。

所以,我的下一个问题是,如果我不能使用JwtBearerEvents OnAuthenticationFailed添加消息,那么添加错误消息的正确位置在哪里?