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

时间:2018-04-01 02:21:23

标签: asp.net-core f# jwt

我一直在使用F#制作网络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)

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

2 个答案:

答案 0 :(得分:2)

终于弄明白了。 F#没有使用=进行分配,它使用<-。因此需要将我的服务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

现在一切正常。

答案 1 :(得分: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);

希望它有所帮助。