【问题标题】:JWT token exception in ASP.Net (Lifetime validation failed. The token is missing an Expiration Time.)ASP.Net 中的 JWT 令牌异常(生命周期验证失败。令牌缺少过期时间。)
【发布时间】:2017-10-13 05:45:00
【问题描述】:

我正在 ASP 上创建自己的自定义身份验证。部署在 Azure 上的 Net MobileService。我使用 JWT 令牌。以下是我生成新令牌的方式(claimType = email):

    public static string GetSecurityToken(String email)
    {
        var symmetricKey = Convert.FromBase64String(signingKey);
        var tokenHandler = new JwtSecurityTokenHandler();

        var now = DateTime.UtcNow;
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
                    {
                    new Claim(ClaimTypes.Email, email)
                }),
            NotBefore = now,
            Expires = now.AddYears(10),
            Issuer = issuer,
            Audience = audience,
            IssuedAt = now,
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(symmetricKey),
                SecurityAlgorithms.HmacSha256Signature),
        };

        var stoken = tokenHandler.CreateToken(tokenDescriptor);
        var token = tokenHandler.WriteToken(stoken);

        return token;
    }

令牌被发送到客户端并存储。但是当我尝试根据其令牌授权消息时,我收到错误:

生命周期验证失败。令牌缺少过期时间。

这是我尝试验证令牌的方式:

    public static ClaimsPrincipal GetPrincipal(string token)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();                
            var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
            
            if (jwtToken == null)
                return null;

            var symmetricKey = Convert.FromBase64String(signingKey);

            Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Validating Token: {0}", token));
            foreach (Claim claim in jwtToken.Claims)
            {
                Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Claims: {0}", claim.ToString()));
            }
            
            var validationParameters = new TokenValidationParameters()
            {
                //RequireExpirationTime = true,
                //ValidateLifetime = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                IssuerSigningKey = new SymmetricSecurityKey(symmetricKey),
            };
            
            SecurityToken securityToken;
            var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
            if (principal != null)
                Debug.WriteLine(String.Format("JWTManager > GetPrincipal > Principal: {0}", principal));
            return principal;
        }
        catch (SecurityTokenException ex)
        {
            Debug.WriteLine(String.Format("JWTManager > GetPrincipal: {0}", ex.Message));
            return null;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(String.Format("JWTManager > GetPrincipal: {0}", ex.Message));
            return null;
        }
    }

执行tokenHandler.ValidateToken时抛出异常,principal返回null。

我的假设是我可能没有正确设置 ExpiresIssuers 属性,并且 TokenHanlder 无法验证它们。但是,当我检查 jwtToken 时,所有声明都已正确设置。

这是完整的调试输出:

JWTManager > GetPrincipal > 声明:电子邮件:testEmail@email.com

JWTManager > GetPrincipal > 声明:nbf:1494752301

JWTManager > GetPrincipal > 声明:exp:33051661101

JWTManager > GetPrincipal > 声明:iat:1494752301

JWTManager > GetPrincipal > 声明:ISS:MASKED

JWTManager > GetPrincipal > 声明:aud: MAKSED

JWTManager > GetPrincipal:IDX10225:生命周期验证失败。令牌缺少过期时间。应用:令牌类型:

【问题讨论】:

  • 我认为你的EXP计算有问题,当我转换它时,结果是Timestamp Converter 33051661101 Is equivalent to: 05/14/3017 @ 8:58am (UTC)(结果来自unixtimestamp.com/index.php)。也许验证不接受未来 1000 年的值?
  • 有趣。所以我现在应该切换。AddYears(10) 到别的东西。嗯……什么都想不起来。不是增加了 10 年,而是增加了 1000 年。
  • 看看我的分析器:stackoverflow.com/questions/43593074/jwt-validation-fails/… 它解释了时间戳是如何定义的。
  • 谢谢。它奏效了。

标签: asp.net authentication authorization jwt lifetime


【解决方案1】:

@aha,看来您通过将到期日期时间缩短到一年后解决了您的问题。哪个可行,但是如果您想了解它之前失败的底层架构原因(并因此在您自己的代码中进行适当的架构更改),您可以在此处阅读此 SO 帖子:https://stackoverflow.com/a/46654832/1222775

最重要的是,针对 Microsoft owin 中间件验证的 JWT 的过期日期上限为 2147483647(也恰好是 Int32.MaxValue),即:Tue, 19 Jan 2038 03:14:07 GMT

在您的 SO 问题中,您发布的显示您使用的“exp”声明值的调试输出是 33051661101 的值,转换为:Wednesday, May 14, 3017 8:58:21 AM,它超过了 Microsoft 的 exp 值上限近 80年:)。

我希望微软能尽快解决这个问题,但与此同时,对于遇到类似问题的任何人,尽量不要发行太长时间的令牌,或者至少不要让它超过 2038 年 1 月 19 日星期二 @格林威治标准时间 03:14:07 :)。

【讨论】:

    【解决方案2】:

    按照here的建议,我通过切换使用解决了这个问题

    System.IdentityModel.Tokens.Jwt.TokenHandler.CreateToken(SecurityTokenDescriptor)

    new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(JwtHeader, JwtPayload).

    并定义payload如下:

    DateTime centuryBegin = new DateTime(1970, 1, 1);
    var exp = new TimeSpan(DateTime.Now.AddYears(1).Ticks - centuryBegin.Ticks).TotalSeconds;
    var now = new TimeSpan(DateTime.Now.Ticks - centuryBegin.Ticks).TotalSeconds;
    var payload = new System.IdentityModel.Tokens.Jwt.JwtPayload
    {
        {"iss", issuer},
        {"aud", audience},
        {"iat", (long)now},
        {"exp", (long)exp}
    };
    

    所以,我最终没有使用 SecurityTokenDescriptor 类,因为它希望将 DateTime 对象分配给 ExpirsIssuedAtLifetime 属性(取决于它是在 Microsoft.IdentityModel.Tokens 还是 System.IdentityModel.Tokens命名空间)。

    我无意使用 SecurityTokenDescriptor;但是,我找不到关于如何使用 SecurityTokenDescriptor 并且仍然为“exp”字段设置正确值的解决方案。

    【讨论】:

      猜你喜欢
      • 2018-12-23
      • 2021-11-11
      • 2020-11-06
      • 2019-06-17
      • 2016-09-17
      • 2021-06-04
      • 2019-08-04
      • 2019-10-20
      • 2014-02-05
      相关资源
      最近更新 更多