【问题标题】:Configure ASP.NET Core authentication middleware to always return 401 when Authorization header is invalid将 ASP.NET Core 身份验证中间件配置为在 Authorization 标头无效时始终返回 401
【发布时间】:2020-02-08 11:22:49
【问题描述】:

我创建了一个默认的 ASP.NET Core (2.1) 空 Web 应用程序,并添加了 JWT 不记名身份验证。 Startup.cs 类如下所示:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            var keyByteArray = Convert.FromBase64String(Constants.JwtSecretKey);
            var signinKey = new SymmetricSecurityKey(keyByteArray);
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = true,
                ValidAudience = Constants.Audience,
                ValidateIssuer = true,
                ValidIssuer = Constants.Issuer,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signinKey
            };
        });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    app.UseMvc();
}

控制器如下所示:

[Route("values")]
[ApiController]
public class ValuesController : Controller
{
    [HttpGet("")]
    public IActionResult Get()
    {
        return new StatusCodeResult(StatusCodes.Status200OK);
    }
}

我希望我的端点在授权标头存在但无效(带有包含失败原因的错误消息)时返回 401 HTTP 状态代码 - 但在标头丢失时不返回。可以这样配置中间件吗?

我尝试处理来自 JwtBearerEvents 的 OnAuthenticationFailed 事件,但无法完成任何操作。

options.Events = new JwtBearerEvents()
{
    OnAuthenticationFailed = context =>
    {
        // Not fired when the Authorization header is "Bearer foo",
        // but fired when the header is "Bearer foo.bar.baz"
        return Task.CompletedTask;
    }
};

【问题讨论】:

    标签: c# asp.net-core jwt


    【解决方案1】:

    根据document,JwtBearerEvents 仅支持以下 4 种事件

    1. OnAuthenticationFailed(这一项仅在 Token 无法通过身份验证后触发,在您的情况下,我认为您需要验证而不是身份验证)
    2. 挑战赛
    3. OnMessageReceived
    4. OnTokenValidated(但这个只有在成功验证 Token 后才会触发,这就是为什么我认为它不适用于您的情况)

    你想要的应该是类似 OnTokenValidateFailed 但它不存在,一种解决方法是注册 OnMessageReceived 事件并尝试在那里验证令牌

    services.AddAuthentication("Bearer")
                    .AddJwtBearer("Bearer", options =>
                    {
                        options.Authority = "http://localhost:5000";
                        options.RequireHttpsMetadata = false;
                        options.Audience = "api1";
                        options.Events=new JwtBearerEvents(){
                            OnMessageReceived  =context=>{
                                var header =context.Request.Headers["Authorization"];
                                //Your validation logic here
                                //if validate failed
                                //{                           
                                //context.Response.StatusCode=401; 
                                //}                          
                                return Task.CompletedTask;
                            }
                        };
                    });
    

    这样您可以自己验证令牌,但是这种方法可能不会使整个请求管道短路,并且可能与其他中间件冲突(未经测试与其他中间件测试,但我认为这可能会发生)

    另一种解决方法如下所示

    在您的 API 项目的 start.cs 配置中添加以下代码

    app.Use(async(context,next)=>{
                    var authHeader=context.Request.Headers["Authorization"];
                    //Your validation here
                    if(validation failed)
                    {
                      context.Response.StatusCode=401;
                      await context.Response.CompleteAsync();
                    }
                    else{
                    await next.Invoke();}
                });
    

    在 UseAuthenticate 之前添加这个以覆盖默认的 Authernticate 中间件

    第一个 workaournd 将在 JwtBearerToken 身份验证时触发,而第二个 workaournd 将始终触发,因为它已在 asp.net 核心中间件管道中注册,因此您可以选择使用哪一个

    【讨论】:

    • 当我诉诸于直接写入现有中间件扩展点中的Response 对象时(例如在您的第一个解决方法中),我总是认为我做错了。使用 ASP.NET 中间件的工作方式,总是麻烦多于其价值,导致异常很难重现(例如,当管道中的某些其他组件此时已经开始写入响应的非常特定的场景时)。
    • 第一个是在 auth 中间件中修改 http 响应,所以您的担心是有道理的,但是您可以将第二个放在 userouting 之后和 useauthentication 之前,这样您就可以尽快短路请求发现令牌无效。
    • 你是对的,这就是为什么我指定“例如在你的第一个解决方法中”:) 另一方面,第二个解决方法将导致执行 JWT 验证两次,以防令牌有效(一次在自定义中间件中,一次在 .NET 身份验证中间件中)。另外,您确定代码应该在验证失败时调用next 委托吗?尽管如此,我明白了这个想法,它应该可以工作。
    • 我编辑了我的评论,是的,在验证失败后不应调用 invoke.next。
    • 您也可以尝试 App.UseWhen 来更好地优化仅在有令牌时才验证令牌的性能,请参阅示例here
    猜你喜欢
    • 2021-10-14
    • 2021-07-16
    • 2018-08-07
    • 2023-04-07
    • 2015-04-08
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    • 2018-01-30
    相关资源
    最近更新 更多