ASP.NET JWT:签名验证失败。没有提供安全密钥来验证签名

Posted

技术标签:

【中文标题】ASP.NET JWT:签名验证失败。没有提供安全密钥来验证签名【英文标题】:ASP.NET JWT: Signature validation failed. No security keys were provided to validate the signature 【发布时间】:2018-09-10 15:54:59 【问题描述】:

我一直在 F# 中制作一个 web api,主要遵循本指南:https://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/。但是,每当我尝试在我的 aspnet webapi 中访问经过身份验证的端点时,我都会收到此错误:

Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiciIsImV4cCI6IjE1MjI2MzUwNDMiLCJuYmYiOiIxNTIyNTQ4NjQzIn0.VofLygSMitkmEsTBFNG-7-3jMAZYkyvfwc2UIs7AIyw.
    Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
       at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
       at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
       at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()

我在这里发现了类似的问题,但没有一个解决方案对我有帮助。我的Startup.fs 看起来像:

type Startup private () =
    new (configuration: IConfiguration) as this =
        Startup() then
        this.Configuration <- configuration

    // This method gets called by the runtime. Use this method to add services to the container.
    member this.ConfigureServices(services: IServiceCollection) =
        // Add framework services
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(fun options ->
            options.TokenValidationParameters = TokenValidationParameters (
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")), 
                ValidateLifetime = false, //validate the expiration and not before values in the token
                ClockSkew = TimeSpan.FromMinutes(5.0) //5 minute tolerance for the expiration date
            ) |> ignore
        ) |> ignore
        services.AddMvc() |> ignore
        services.AddSwaggerGen (fun c -> c.SwaggerDoc("v1", Swagger.Info()))  |> ignore
        services.AddCors() |> ignore


    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    member this.Configure(app: IApplicationBuilder, env: IHostingEnvironment) =
        app.UseExceptionHandler(
            fun options ->
                options.Run(
                    fun context ->
                        let ex = context.Features.Get<IExceptionHandlerFeature>()
                        match ex.Error with
                        | HttpCodedException (code, message) ->
                             printfn "code: %i, msg: %s" (int code) message
                             context.Response.StatusCode <- int code
                             context.Response.WriteAsync(message)
                        | exn -> raise (exn)
                )
        ) |> ignore

        // let cors = Action<CorsPolicyBuilder> (fun builder -> builder.WithOrigins("http://localhost:3000").AllowAnyHeader().AllowAnyMethod() |> ignore)
        app.UseCors(fun policy ->
            policy.AllowAnyHeader()
                    .AllowAnyOrigin()
                    .AllowCredentials()
                    .AllowAnyMethod()
                    .Build() |> ignore
        ) |> ignore

        app.UseAuthentication() |> ignore 

        app.UseMvc() |> ignore


    member val Configuration : IConfiguration = null with get, set

我已经尝试关闭基本上所有的验证,所以我很困惑为什么这仍然失败。如果有帮助,我生成令牌的地方如下所示:

let GenerateToken (username) =
    let claims = [|
        Claim (ClaimTypes.Name, username)
        Claim (JwtRegisteredClaimNames.Exp, DateTimeOffset(DateTime.Now.AddDays(1.0)).ToUnixTimeSeconds().ToString())
        Claim (JwtRegisteredClaimNames.Nbf, DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString())
    |]
    let cred =
        new SigningCredentials(
            SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
            SecurityAlgorithms.HmacSha256
        )
    let token = JwtSecurityToken(JwtHeader(cred), JwtPayload(claims))
    JwtSecurityTokenHandler().WriteToken(token)

希望有人能看到我做错了什么。

【问题讨论】:

【参考方案1】:

这对我来说效果很好。

JWT 身份验证设置

services.AddAuthentication(options =>

    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
).AddJwtBearer(options =>

    options.RequireHttpsMetadata = false;
    options.TokenValidationParameters = new TokenValidationParameters()
    
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("thisKeyIs32CharactersLong1234567"))
        ValidateIssuer = true,
        ValidIssuer = "MyIssuer",
        ValidateAudience = true,
        ValidAudience = "MyAudience",
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero
    ;
);

然后创建实际的令牌

var handler = new JwtSecurityTokenHandler();
var securityToken = handler.CreateToken(
    new SecurityTokenDescriptor
    
        Issuer = "MyIssuer",
        Audience = "MyAudience",
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("thisKeyIs32CharactersLong1234567")), SecurityAlgorithms.HmacSha512Signature),
        Subject = new ClaimsIdentity(    
            new[] 
                new Claim(ClaimTypes.Name, "My Name"),
                new Claim(ClaimTypes.Sid, "My UID"),
                new Claim(ClaimTypes.GroupSid, "My GID")
            ,
        Expires = DateTime.Now + TimeSpan.FromMinutes("30")                
    );

// Save token
handler.WriteToken(securityToken);

希望对你有帮助。

【讨论】:

很好的答案,但需要注意的是,对于生产环境,RequireHttpsMetadata 应该设置为 true。【参考方案2】:

终于想通了。 F# 不使用= 进行赋值,它使用&lt;-。因此需要将我的服务 AddAuthenticaton 调用更改为:

    services.AddAuthentication(fun options ->
        options.DefaultScheme <- JwtBearerDefaults.AuthenticationScheme
        options.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme 
        options.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme
    ).AddJwtBearer(fun options ->
        options.TokenValidationParameters <- TokenValidationParameters (
            ValidateAudience = false,
            ValidateIssuer = false,
            ValidateIssuerSigningKey = false,
            IssuerSigningKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")), 
            ValidateLifetime = false, //validate the expiration and not before values in the token
            ClockSkew = TimeSpan.FromMinutes(5.0), //5 minute tolerance for the expiration date
            ValidateActor = false,
            ValidateTokenReplay = false
        )
    ) |> ignore

现在一切正常。

【讨论】:

刚刚做了同样的事情。疯狂的是我花了多长时间才意识到。感谢您的帮助!

以上是关于ASP.NET JWT:签名验证失败。没有提供安全密钥来验证签名的主要内容,如果未能解决你的问题,请参考以下文章

ASP.net Core 2.0 AzureAd Bearer JWT-Token Auth 在验证签名时不会失败

ASP.NET Core 5 JWT 身份验证失败,响应代码为 401

JwtSecurityTokenHandler ValidateToken:“签名验证失败。没有提供安全密钥来验证签名”

ASP.NET Core 2.0 JWT 验证失败并出现“用户授权失败:(null)”错误

JWT/OAuth 令牌签名验证失败

ASP.NET Core API 使用 JWT 不记名令牌进行身份验证