ASP.Net中的JWT令牌异常(生命周期验证失败。令牌缺少过期时间。)

问题描述 投票:4回答:2

我在ASP上创建自己的自定义身份验证。 Net Mobile部署在Azure上。我使用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时抛出异常,并返回null到principal

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

这是完整的调试输出:

JWTManager> GetPrincipal>声明:电子邮件:[email protected]

JWTManager> GetPrincipal>声明:nbf:1494752301

JWTManager> GetPrincipal> Claims:exp:33051661101

JWTManager> GetPrincipal>声明:iat:1494752301

JWTManager> GetPrincipal>声明:iss:MASKED

JWTManager> GetPrincipal>索赔:aud:PAYMENTS

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

asp.net authentication authorization jwt lifetime
2个回答
13
投票

@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,它超过了微软的exp值上限近80年:)。

我希望微软能尽快解决这个问题,但是对于遇到类似问题的人来说,同时不要发出太长时间的令牌,或者至少不要让它超过Tue,2038年1月19日@ 03:14 :07 GMT :)。


3
投票

根据here的建议,我通过切换使用来解决问题

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

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

并将有效负载定义如下:

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对象分配给ExpirsIssuedAt,或Lifetime属性(取决于它是否在Microsoft.IdentityModel.TokensSystem.IdentityModel.Tokens命名空间中)。

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

© www.soinside.com 2019 - 2024. All rights reserved.