JwtSecurityToken在它应该的时候不会过期

时间:2016-09-27 15:23:47

标签: c# .net jwt

我目前正在使用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和此库。

提前谢谢

6 个答案:

答案 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更新

这在.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

希望这会有所帮助。