JwtSecurityToken 过期时间无效 .NET Core 3.1
Posted
技术标签:
【中文标题】JwtSecurityToken 过期时间无效 .NET Core 3.1【英文标题】:JwtSecurityToken Expiry Time Invalid .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);
【问题讨论】:
【参考方案1】:您的令牌包含时间戳nbf
和exp
作为字符串:
"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), ...)
【讨论】:
以上是关于JwtSecurityToken 过期时间无效 .NET Core 3.1的主要内容,如果未能解决你的问题,请参考以下文章
System.IdentityModel.Tokens.JwtSecurityToken 自定义属性