ASP.NET Core 2.0 JWT 验证失败,出现“用户授权失败:(null)”错误
ASP.NET Core 2.0 JWT Validation fails with `Authorization failed for user: (null)` error
我正在使用 ASP.NET Core 2.0 应用程序(Web API)作为 JWT 发行者来生成移动应用程序可使用的令牌。不幸的是,此令牌无法由一个控制器验证,但可以由另一个控制器验证(在同一 asp.net 核心 2.0 应用程序中使用相同的验证设置)。
所以我有一个有效且可以解码的令牌,具有所有必需的声明和时间戳。但是一个端点接受它,而另一个端点给我 401 错误和调试输出:
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information:
Authorization failed for user: (null).
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed for user: (null).
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 271.077ms 401
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 271.077ms 401
我的验证设置如下:
var secretKey = Configuration["Authentication:OAuth:IssuerSigningKey"];
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = Configuration["Authentication:OAuth:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["Authentication:OAuth:Audience"],
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenValidationParameters;
});
这两个端点是相同的,只是位于不同的控制器中,都标有 Authorize
属性。
这怎么可能?
您可以试试这个:
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenValidationParameters;
});'
在 startup.cs
试试这个
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opts => ...
我补充了:
app.UseAuthentication();
在 Startup.Configure()
中为我解决了这个错误。
配置函数中添加语句的顺序很重要。确保
app.UseAuthentication();
在
之前
app.UseMvc();
这可能是问题所在吗?
这似乎是您在未正确验证 JWT 时收到的行为。由于在 header
中键入 "Bearer: (JWT)" 而不是 "Bearer (JWT)",我遇到了这个问题
在您的 startup.cs ConfigureServices 方法中,如果您添加
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => ...
解释:
当您在控制器上使用 [Authorize] 时,它默认绑定到第一个授权系统。
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
有了这个,您将默认设置为 JWT Bearer 身份验证。
此外,您还可以添加
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
这一行是如何防止在将 Identity 与 JWT 结合使用时出现 404 not found 错误。如果您使用的是身份,则 DefaultChallengeScheme 将尝试将您重定向到登录页面,如果该页面不存在,将导致未找到 404 而不是想要的 401 未授权。通过在未经授权时将 DefaultChallengeScheme 设置为 JwtBearerDefaults.AuthenticationScheme,它将不再尝试将您重定向到登录页面
如果您在 [Authorize] 标签中使用带有 JWT 身份验证的 Cookie 身份验证,您可以指定所需的身份验证方案。例如
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
当像这样添加身份验证时:
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
....
这意味着放在方法或控制器 class 之上的每个属性 [Authorize] 都将尝试根据默认身份验证模式(在本例中为 JwtBearer)进行身份验证 并且它不会向下级联 尝试使用可能声明的其他模式(如 Cookie 模式)进行身份验证。为了使 AuthorizeAttribute 根据 cookie 模式进行身份验证,必须像
一样指定它
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
反之亦然,即如果 cookie 模式是默认的,则必须声明 JwtBearer 模式以授权那些需要 JwtBearer 令牌身份验证的方法或控制器
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
检查您的令牌提供商中的签名密钥编码,例如可以是 UTF8 而不是 ASCII。
对于 Dotnetcore 3.1,我在 app.UseAuthorization()
之前放置了 app.UseAuthentication()
对于任何使用 .NET Core 3.1
的人来说,它是这样工作的:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("AllowOrigin");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
如果有人无法回答涉及 IServiceCollection.AddAuthentication
的配置或 Startup.Configure
的实施,您可以尝试的一件事是更改 IServiceCollection.AddAuthorization
的配置。
在进行此更改之前,使用正确的令牌调用 API 失败并显示以下日志行。
[Information] [Microsoft.AspNetCore.Hosting.Diagnostics] Request starting HTTP/1.1 POST http://localhost:5000/api application/json 18
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] 1 candidate(s) found for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] Endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"' with route pattern '"Api"' is valid for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware] Request matched endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"'
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Information] [Microsoft.AspNetCore.Authorization.DefaultAuthorizationService] Authorization failed.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] Successfully validated the token.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] AuthenticationScheme: "Bearer" was challenged.
[Information] [Serilog.AspNetCore.RequestLoggingMiddleware] HTTP "POST" "/api" responded 401 in 4.6311 ms
在 Startup.ConfigureServices
中,一旦我应用了指定身份验证方案的默认策略,它就对我有用。
services.AddAuthorization(opt =>
{
var builder = new AuthorizationPolicyBuilder();
builder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builder.RequireAuthenticatedUser();
opt.DefaultPolicy = builder.Build();
});
我正在使用 ASP.NET Core 2.0 应用程序(Web API)作为 JWT 发行者来生成移动应用程序可使用的令牌。不幸的是,此令牌无法由一个控制器验证,但可以由另一个控制器验证(在同一 asp.net 核心 2.0 应用程序中使用相同的验证设置)。
所以我有一个有效且可以解码的令牌,具有所有必需的声明和时间戳。但是一个端点接受它,而另一个端点给我 401 错误和调试输出:
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed for user: (null).
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 271.077ms 401
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 271.077ms 401
我的验证设置如下:
var secretKey = Configuration["Authentication:OAuth:IssuerSigningKey"];
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = Configuration["Authentication:OAuth:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["Authentication:OAuth:Audience"],
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenValidationParameters;
});
这两个端点是相同的,只是位于不同的控制器中,都标有 Authorize
属性。
这怎么可能?
您可以试试这个:
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenValidationParameters;
});'
在 startup.cs
试试这个services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opts => ...
我补充了:
app.UseAuthentication();
在 Startup.Configure()
中为我解决了这个错误。
配置函数中添加语句的顺序很重要。确保
app.UseAuthentication();
在
之前app.UseMvc();
这可能是问题所在吗?
这似乎是您在未正确验证 JWT 时收到的行为。由于在 header
中键入 "Bearer: (JWT)" 而不是 "Bearer (JWT)",我遇到了这个问题在您的 startup.cs ConfigureServices 方法中,如果您添加
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => ...
解释: 当您在控制器上使用 [Authorize] 时,它默认绑定到第一个授权系统。
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
有了这个,您将默认设置为 JWT Bearer 身份验证。
此外,您还可以添加
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
这一行是如何防止在将 Identity 与 JWT 结合使用时出现 404 not found 错误。如果您使用的是身份,则 DefaultChallengeScheme 将尝试将您重定向到登录页面,如果该页面不存在,将导致未找到 404 而不是想要的 401 未授权。通过在未经授权时将 DefaultChallengeScheme 设置为 JwtBearerDefaults.AuthenticationScheme,它将不再尝试将您重定向到登录页面
如果您在 [Authorize] 标签中使用带有 JWT 身份验证的 Cookie 身份验证,您可以指定所需的身份验证方案。例如
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
当像这样添加身份验证时:
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
....
这意味着放在方法或控制器 class 之上的每个属性 [Authorize] 都将尝试根据默认身份验证模式(在本例中为 JwtBearer)进行身份验证 并且它不会向下级联 尝试使用可能声明的其他模式(如 Cookie 模式)进行身份验证。为了使 AuthorizeAttribute 根据 cookie 模式进行身份验证,必须像
一样指定它[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
反之亦然,即如果 cookie 模式是默认的,则必须声明 JwtBearer 模式以授权那些需要 JwtBearer 令牌身份验证的方法或控制器
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
检查您的令牌提供商中的签名密钥编码,例如可以是 UTF8 而不是 ASCII。
对于 Dotnetcore 3.1,我在 app.UseAuthorization()
app.UseAuthentication()
对于任何使用 .NET Core 3.1
的人来说,它是这样工作的:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("AllowOrigin");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
如果有人无法回答涉及 IServiceCollection.AddAuthentication
的配置或 Startup.Configure
的实施,您可以尝试的一件事是更改 IServiceCollection.AddAuthorization
的配置。
在进行此更改之前,使用正确的令牌调用 API 失败并显示以下日志行。
[Information] [Microsoft.AspNetCore.Hosting.Diagnostics] Request starting HTTP/1.1 POST http://localhost:5000/api application/json 18
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] 1 candidate(s) found for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] Endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"' with route pattern '"Api"' is valid for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware] Request matched endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"'
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Information] [Microsoft.AspNetCore.Authorization.DefaultAuthorizationService] Authorization failed.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] Successfully validated the token.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] AuthenticationScheme: "Bearer" was challenged.
[Information] [Serilog.AspNetCore.RequestLoggingMiddleware] HTTP "POST" "/api" responded 401 in 4.6311 ms
在 Startup.ConfigureServices
中,一旦我应用了指定身份验证方案的默认策略,它就对我有用。
services.AddAuthorization(opt =>
{
var builder = new AuthorizationPolicyBuilder();
builder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builder.RequireAuthenticatedUser();
opt.DefaultPolicy = builder.Build();
});