.net JwtSecurityToken在到期时未到期

0s7z1bwu  于 2022-12-27  发布在  .NET
关注(0)|答案(6)|浏览(217)

我当前使用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);

由于某种原因,即使过期时间设置为当前时间之后10秒,但在验证令牌时,直到大约5分钟后才会抛出异常。看到这个消息后,我想可能存在最短5分钟的过期时间,所以我将过期时间设置为:

DateTime.UtcNow.AddMinutes(5);

然后,它在10分钟后到期,但异常消息指出到期时间设置为预期的时间(用户登录后5分钟),而当它在异常中显示当前时间时,它是过期时间后5分钟。因此,它似乎知道它应该在什么时候过期,但实际上直到过期时间后5分钟才抛出异常。由于令牌似乎在我设置的到期时间基础上增加了5分钟,因此我将到期时间设置为:

DateTime.UtcNow.AddMinutes(-5).AddSecond(10);

我测试了这个,到目前为止它还没有过期(十多分钟后)。有人能解释一下为什么会发生这种情况,我做错了什么吗?另外,如果你看到我提供的代码有任何其他的地方,任何指导都将不胜感激,因为我是第一次使用JWT和这个库。

nr7wwzry

nr7wwzry1#

这个问题与ClockSkew有关。通常,验证库(至少是MS库)会补偿时钟偏移。ClockSkew默认值为5分钟。请参见here的一些答案
您可以在TokenValidationParameters中更改ClockSkew

var tokenValidationParameters = new TokenValidationParameters
{
    //...your setting

    // set ClockSkew is zero
    ClockSkew = TimeSpan.Zero
};

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = tokenValidationParameters
});
w46czmvw

w46czmvw2#

在阅读了@Denis Kucherov的回答之后,我发现我可以使用他发布的相同的自定义验证器,而不需要使用JwtBearerOptions类,因为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;
}
xuo3flqw

xuo3flqw3#

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;
}
nmpmafwu

nmpmafwu4#

.NET核心更新

在.NET Core中,处理方式略有不同,因为TokenValidationParameters是使用ConfigureServices()方法在Startup.cs中设置的,然后由中间件自动处理。
还请注意,旧的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);
    }
}
0aydgbwb

0aydgbwb5#

我也刚刚实现了一个JWT令牌中间件,虽然互联网上的示例使用UtcNow,但我必须使用Now,否则过期时间是关闭的。

xn1cxnb4

xn1cxnb46#

下面的链接给予你确切的答案,默认情况下MS有5分钟的到期时间。所以要么你必须使它自定义或时间,你会在到期:DateTime.Now.AddSeconds(30)上一行中的30秒将添加到过期时间中。因此,总过期时间将为5分30秒
https://github.com/IdentityServer/IdentityServer3/issues/1251
希望这会有帮助。

相关问题