我目前正在使用System.IdentityModels.Tokens命名空间中的JwtSecurityToken类。我使用以下内容创建令牌:
DateTime expires = DateTime.UtcNow.AddSeconds(10);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var genericIdentity = new System.Security.Principal.GenericIdentity(username, "TokenAuth");
ClaimsIdentity identity = new ClaimsIdentity(claims);
string secret = ConfigurationManager.AppSettings["jwtSecret"].ToString();
var securityKey = new InMemorySymmetricSecurityKey(Encoding.Default.GetBytes(secret));
var signingCreds = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.HmacSha256Signature);
var securityToken = handler.CreateToken(
issuer: issuer,
audience: ConfigurationManager.AppSettings["UiUrl"].ToString(),
signingCredentials: signingCreds,
subject: identity,
expires: expires,
notBefore: DateTime.UtcNow
);
return handler.WriteToken(securityToken);
由于某种原因,即使将expires设置为当前时间之后的10秒,它也不会在令牌被验证之前实际抛出异常,直到大约5分钟。看到这个之后,我想也许最短的过期时间是5分钟,所以我将过期时间设置为:
DateTime.UtcNow.AddMinutes(5);
然后它在10分钟后到期,但异常消息表明过期时间设置为应该是的(用户登录后5分钟),当它显示异常中的当前时间时到期后5分钟。因此,它似乎知道什么时候它应该过期,但它实际上并没有在过期时间后5分钟抛出异常。然后,由于令牌似乎在我将其设置为过期的任何时间增加了5分钟,我将过期时间设置为:
DateTime.UtcNow.AddMinutes(-5).AddSecond(10);
我测试了这个,到目前为止它还没有过期(超过十分钟后)。有人可以解释为什么会发生这种情况以及我做错了什么?此外,如果您看到我提供的代码还有其他任何指导,我将不胜感激,因为我不熟悉使用JWT和此库。
提前谢谢
答案 0 :(得分:29)
问题与ClockSkew
有关。通常,验证库(至少MS一个)补偿时钟偏差。 ClockSkew
默认值为5分钟。请参阅回答here
您可以在ClockSkew
中更改TokenValidationParameters
:
var tokenValidationParameters = new TokenValidationParameters
{
//...your setting
// set ClockSkew is zero
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
快乐的编码!
答案 1 :(得分:6)
LifeTimeValidator似乎存在一些问题。您可以使用自定义委托覆盖其逻辑。此外,使用JwtBearerOptions类来控制身份验证中间件行为。例如:
new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuer = _configuration["Tokens:Issuer"],
ValidAudience = _configuration["Tokens:Audience"],
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
LifetimeValidator = LifetimeValidator,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"]))
}
}
并分配LifetimeValidotor委托,以提供自己的超时验证逻辑:
private bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken token, TokenValidationParameters @params)
{
if (expires != null)
{
return expires > DateTime.UtcNow;
}
return false;
}
答案 2 :(得分:5)
在阅读了@Denis Kucherov的回答之后,我发现我可以使用他发布的相同自定义验证器而不使用JwtBearerOptions类,这将要求我添加一个新库。
此外,由于有两个名称空间包含许多这些相同的类,我将确保提及所有这些都使用System.IdentityModels ...名称空间。 (不是Microsoft.IdentityModels ...)
以下是我最终使用的代码:
private bool CustomLifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken tokenToValidate, TokenValidationParameters @param)
{
if (expires != null)
{
return expires > DateTime.UtcNow;
}
return false;
}
private JwtSecurityToken ValidateJwtToken(string tokenString)
{
string secret = ConfigurationManager.AppSettings["jwtSecret"].ToString();
var securityKey = new InMemorySymmetricSecurityKey(Encoding.Default.GetBytes(secret));
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validation = new TokenValidationParameters()
{
ValidAudience = "MyAudience",
ValidIssuer = "MyIssuer",
ValidateIssuer = true,
ValidateLifetime = true,
LifetimeValidator = CustomLifetimeValidator,
RequireExpirationTime = true,
IssuerSigningKey = securityKey,
ValidateIssuerSigningKey = true,
};
SecurityToken token;
ClaimsPrincipal principal = handler.ValidateToken(tokenString, validation, out token);
return (JwtSecurityToken)token;
}
答案 3 :(得分:3)
这在.NET Core中的处理稍有不同,因为使用TokenValidationParameters
方法在Startup.cs
中设置了ConfigureServices()
,然后由中间件自动处理。
还请注意,较早的InMemorySymmetricSecurityKey
用于签署秘密is now deprecated,而倾向于SymmetricSecurityKey
,如下所示。
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _config.AuthenticationSettings.TokenAuthority,
ValidAudience = _config.AuthenticationSettings.TokenAuthority,
LifetimeValidator = TokenLifetimeValidator.Validate,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_config.AuthenticationSettings.SecurityKey))
};
});
// ...
}
因此,我还在@tkd_aj的answer above中制作了自己的令牌验证器版本,并将其扔到了静态类中:
public static class TokenLifetimeValidator
{
public static bool Validate(
DateTime? notBefore,
DateTime? expires,
SecurityToken tokenToValidate,
TokenValidationParameters @param
) {
return (expires != null && expires > DateTime.UtcNow);
}
}
答案 4 :(得分:0)
我刚刚实现了JWT令牌中间件,虽然互联网上的示例使用UtcNow
,但我必须使用Now
或过期时间已关闭。当我使用Now
时,过期就会被发现。
答案 5 :(得分:0)
下面的链接为您提供确切的答案,因为默认情况下,MS的过期时间为5分钟。 所以要么您必须自定义它,要么您要付出时间 到期:DateTime.Now.AddSeconds(30) 以上行中的30秒将被添加为过期时间。因此,总过期时间将为5分钟30秒
https://github.com/IdentityServer/IdentityServer3/issues/1251
希望这会有所帮助。