【问题标题】:JwtSecurityToken Expiry Time Invalid .NET Core 3.1JwtSecurityToken 过期时间无效 .NET Core 3.1
【发布时间】:2021-07-05 11:20:13
【问题描述】:

我将我的应用程序从 .NET Core 2.2 升级到了 .NET Core 3.1。当我尝试使用 PostMan 测试我的端点时,我注意到我收到了 401 Unauth 错误。当我查看标题时,我发现到期时间无效:

我拿了以下不记名令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQm9iIiwibmJmIjoiMTYxNzk3Nzg1MSIsImV4cCI6IjE2MjMxNjE4NTYiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiQmFza2V0YmFsbCIsIlJ1Z2J5IiwiRm9vdGJhbGwiXX0.QRLuXFeopf7QZ1NUzWcctuSfnNXiPgc2UH7NxAuHYvw

我正在生成令牌端点并使用jwt.io 对其进行解码,exp 字段为“1623161856”。 我将其转换为 Javascript 中的日期对象,它等于未来 60 天。

所以令牌肯定没有过期。不确定我在升级到 .NET Core 3.1 时是否遗漏了任何内容,但这里是相关代码:

Startup.cs我有

   public void ConfigureServices(IServiceCollection services)
    {


        // Initial Setup
        services.AddMvc();
        services.AddSingleton<IConfiguration>(Configuration);
        // Call this in case you need aspnet-user-authtype/aspnet-user-identity
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        // Register the Swagger generator, defining one or more Swagger documents 
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc(Configuration["v1"], new OpenApiInfo { Title = Configuration["Sports"], Version = Configuration["v1] });
        });

        services.AddDataProtection();
        
        //Authentication Setup
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("AnythingYouWant")),
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromMinutes(5)
            };
            options.SaveToken = true;
            options.Events = new JwtBearerEvents()
            {
                OnTokenValidated = context =>
                {
                    var accessToken = context.SecurityToken as JwtSecurityToken;
                    if (accessToken != null)
                    {
                        ClaimsIdentity identity = context.Principal.Identity as ClaimsIdentity;
                        if (identity != null)
                        {
                            identity.AddClaim(new Claim("access_token", accessToken.RawData));
                        }
                    }

                    return Task.CompletedTask;
                }
            };
        });
        services.AddAuthorization();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseSwagger();

        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/" + Configuration["v1"] + "/swagger.json", Configuration["Sports"]);
        });
        
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors();

        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapControllerRoute("swagger", "swagger/");
        });

        app.UseWelcomePage("/swagger");
    }

并且令牌是由我的 api 端点之一生成的。该代码如图所示:

 [HttpPost("SportApi/Token")]
    [ServiceFilter(typeof(SportResourceFilter))]
    public IActionResult Create(string key)
    {
       return new ObjectResult(GenerateToken(key));
    }
 private string GenerateToken(string someKey)
    {
        JwtSecurityToken token = new JwtSecurityToken();
        List<SportAPIKey> ro = new List<SportAPIKey>();

        if (!string.IsNullOrEmpty(someKey))
        {

            using (StreamReader r = new StreamReader("keys.json"))
            {
                string json = r.ReadToEnd();
                ro = JsonConvert.DeserializeObject<List<SportAPIKey>>(json);
            }
            
            if (ro.Exists(sak => sak.SportAPIKeyValue.Equals(someKey)))
            {
                SportAPIKey sportapikey = ro.Find(sak => sak.SportAPIKeyValue.Equals(someKey));
                List<Claim> lc = new List<Claim>();
                Claim claimClient = new Claim(ClaimTypes.Name, sportapikey.Client);
                lc.Add(claimClient);
                foreach (string team in sportapikey.Teams)
                    {
                        lc.Add(new Claim(ClaimTypes.System, team.Trim()));
                    }
                Claim claimEffDate = new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString());
                lc.Add(claimEffDate);
                int tokenLifespan = 60;
                Claim claimExpDate = new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds().ToString());
                lc.Add(claimExpDate);

                foreach (string sport in sportapikey.Sports.Split(","))
                {
                    lc.Add(new Claim(ClaimTypes.Role, sport.Trim()));
                }

                var claims = lc.ToArray();
                
                token = new JwtSecurityToken(
                    new JwtHeader(new SigningCredentials(
                        new SymmetricSecurityKey(Encoding.UTF8.GetBytes("AnythingYouWant")),
                                                 SecurityAlgorithms.HmacSha256)),
                    new JwtPayload(claims));
            }
        }

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

【问题讨论】:

    标签: c# asp.net-core authentication jwt asp.net-authorization


    【解决方案1】:

    您的令牌包含时间戳nbfexp作为字符串:

      "nbf": "1617977851",
      "exp": "1623161856",
    

    这是无效的。在 https:\jwt.io 上,当您将鼠标悬停在这些值上时,您会发现出现了问题。通常它显示时间戳,但在您的示例中它显示:“无效日期”:

    JWT 中的时间戳应该是numerical values

    数字日期
    一个 JSON 数值,表示从 1970-01-01T00:00:00Z UTC 直到指定的 UTC 日期/时间

    值本身是正确的:

    在生成声明期间发生错误:

    Claim claimExpDate = new Claim(JwtRegisteredClaimNames.Exp, 
                                   new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds().ToString());
    lc.Add(claimExpDate);
    

    改为使用this constructor,它可以让您设置ClaimValueType

    new Claim(JwtRegisteredClaimNames.Exp, 
              new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds().ToString(), 
              ClaimValueTypes.Integer64)
    

    或者更好的是,让框架为你添加过期时间戳,使用this constructor

    public JwtSecurityToken (string issuer = default, string audience = default, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims = default, DateTime? notBefore = default, DateTime? expires = default, Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials = default);
    

    例如:

    JwtSecurityToken(expires: DateTime.UtcNow.AddDays(60),  ...)
    

    【讨论】:

      猜你喜欢
      • 2018-05-25
      • 2018-05-22
      • 1970-01-01
      • 1970-01-01
      • 2017-02-05
      • 2021-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多