【发布时间】:2018-04-21 15:48:42
【问题描述】:
我已经在我的 ASP.NET Core 2.0 Web API 中生成 JWT 令牌,但我遇到了一个问题,即后续的新访问令牌与之前生成的令牌具有相同的过期时间。
例如,我发布登录凭据,并返回访问令牌。访问令牌在 [Authorize] API 端点上按预期工作。出于测试目的,我将令牌设置为 1 分钟后过期。 1 分钟后,令牌过期,经过身份验证的端点返回 401,如预期的那样。
我正在我的客户端应用程序中处理 401。登录表单出现,用户再次登录。生成并返回一个新令牌。唯一的问题是,这个新令牌与最初生成的令牌具有完全相同的“ValidTo”日期时间。在使用这个新令牌后导致任何调用返回 401,因为令牌已经过期。我已经确认两个不同的令牌正在检查,所以我传递错误的令牌不是问题
第一个令牌失败(预期,因为令牌已过期):
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:信息:无法验证令牌 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQHBytNDRiYS1...Do1NzM5NS8ifQ.t8DjvlGV7GZ3xucwu-1hlJRXA5yQHHPii>>
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException:IDX10223:生命周期验证失败。令牌已过期。
有效期至:'11/08/2017 19:23:09'
当前时间:'11/08/2017 19:23:13'。
第二个令牌失败(不是预期的,ValidTo 与前一个令牌相同)
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:信息:无法验证令牌 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQ...dDo1NzM5NS8ifQ.2TMPJvYnQl1Jw78M2nj40uD3qejBEciXfKC85saGNI.p>
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException:IDX10223:生命周期验证失败。令牌已过期。
有效期至:'11/08/2017 19:23:09'
当前时间:'11/08/2017 19:23:34'。
Startup.cs 中的 JWT 配置
services.Configure<JwtIssuerOptions>(options => {
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
options.ValidFor = TimeSpan.FromMinutes(1);
});
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = SigningKey,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
};
});
创建Token的登录动作:
[HttpPost]
public async Task<IActionResult> Login([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
// Serialize and return the response
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
auth_token = await _jwtFactory.GenerateEncodedToken(credentials.UserName, identity),
expires_in = (int)_jwtOptions.ValidFor.TotalSeconds
};
var json = JsonConvert.SerializeObject(response, _serializerSettings);
return new OkObjectResult(json);
}
生成令牌的JwtFactory方法:
private readonly JwtIssuerOptions _jwtOptions;
public JwtFactory(IOptions<JwtIssuerOptions> jwtOptions)
{
_jwtOptions = jwtOptions.Value;
ThrowIfInvalidOptions(_jwtOptions);
}
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
identity.FindFirst("rol"),
identity.FindFirst("id")
};
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
【问题讨论】:
标签: asp.net asp.net-web-api jwt