使用C#撤销JWT

时间:2019-02-11 15:08:49

标签: c# jwt

我有一个自定义的Authorize类,当用户向服务器请求数据或任何东西时,我使用该类使令牌无效

,但是每当令牌到期时,原理仍然返回IsAuthenticated为true,并且仍然调用控制器并获取数据。

我想要它做的是使令牌无效并显式注销用户退出系统。我找不到任何帮助。如果需要,我可以提供JWT属性/过滤器的代码

更新1:代币生成

public static string GenerateToken(User user)
{
    int expireMinutes;
    try
    {
        expireMinutes = string.IsNullOrEmpty(ConfigurationManager.AppSettings["SessionTimeInMinutes"])
            ? 30
            : int.Parse(ConfigurationManager.AppSettings["SessionTimeInMinutes"]);
    }
    catch (Exception)
    {
        expireMinutes = 30;
    }
    var symmetricKey = Convert.FromBase64String(Secret);
    var tokenHandler = new JwtSecurityTokenHandler();
    var now = DateTime.Now;
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Email, user.Email)
            ,new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName)
            ,new Claim("uid", user.Id.ToString())
            ,new Claim("cid", user.ClientId.ToString())
            ,new Claim("rid", string.Join(",", user.Roles.Select(r => r.RoleId).ToList()))
        }),
        Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
        IssuedAt = now,
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
    };
    var stoken = tokenHandler.CreateToken(tokenDescriptor);
    var token = tokenHandler.WriteToken(stoken);
    return token;
}

服务器端授权令牌

 public async Task AuthenticateAsync(
        HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        var excludedList = new List<string>();
        excludedList.Add("/api/Security/IsMustChangePassword");
        excludedList.Add("/api/Security/IsTwoFactorEnabled");

        if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
        {
            var request = context.Request;
            var authorization = request.Headers.Authorization;
            if (authorization == null || authorization.Scheme != "Token")
            {
                return;
            }
            if (string.IsNullOrEmpty(authorization.Parameter))
            {
                context.ErrorResult = new AuthenticationFailureResult("Missing Jwt Token", request);
                return;
            }

            //{
            //    context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
            //    return;
            //}
            var token = authorization.Parameter;
            var principal = await AuthenticateJwtToken(token).ConfigureAwait(true);
            var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
            var accountManager = new AccountManager();
            var user = accountManager.GetUserDetails(userId);
            var newToken = JwtManager.GenerateToken(user);

            if (principal == null)
                context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
            else
                context.Principal = principal;

            if (principal.Identity.IsAuthenticated)
            {
                var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
                if ((DateTime.Now - expiryDate).TotalSeconds > 0)
                {
                    context.Request.Headers.Authorization = null;
                    context.Request.RequestUri = null;


                }
                else
                {
                    var authorize = new AuthenticationHeaderValue("token", newToken);
                    context.Request.Headers.Authorization = authorize;
                    context.ActionContext.Request.Headers.Authorization = authorization;
                }
            }



        }

    }

    private static bool ValidateToken(string token, out string username, out 
    string passwordHash)
    {
        username = null;
        passwordHash = null;
        try
        {
            var principle = JwtManager.GetPrincipal(token);
            var identity = principle.Identity as ClaimsIdentity;

            if (identity == null)
                return false;

            if (!identity.IsAuthenticated)
                return false;

            var usernameClaim = identity.FindFirst(ClaimTypes.Name);
            var passwordClaim = identity.FindFirst(ClaimTypes.Hash);

            username = usernameClaim?.Value;
            passwordHash = passwordClaim?.Value;

            return !string.IsNullOrEmpty(username);

            var user = identity.FindFirst(username);
            return (user != null);
            //return (user != null && user.PasswordHash == passwordHash);
        }
        catch (NullReferenceException)
        {
            return false;
        }
    }

    protected Task<IPrincipal> AuthenticateJwtToken(string token)
    {
        string username;
        string passwordHash;
        if (!ValidateToken(token, out username, out passwordHash))
            return Task.FromResult<IPrincipal>(null);
        // based on username to get more information from database in order to build local identity
        var claims = new List<Claim> { new Claim(ClaimTypes.Name, username) };
        //claims.Add(new Claim(ClaimTypes.Hash, passwordHash));
        // Add more claims if needed: Roles, ...

        var identity = new ClaimsIdentity(claims, "Jwt");
        IPrincipal user = new ClaimsPrincipal(identity);
        return Task.FromResult(user);
    }

    public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
        CancellationToken cancellationToken)
    {
        var authorization = context.Request.Headers.Authorization;
        var excludedList =
            new List<string> {
                "/api/Security/IsMustChangePassword",
                "/api/Security/IsTwoFactorEnabled" };
        if (context.Request.Headers.Authorization != null)
        {
            if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
            {
                var token = context.Request.Headers.Authorization.Parameter;
                var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
                var accountManager = new AccountManager();
                var user = accountManager.GetUserDetails(userId);
                var newToken = JwtManager.GenerateToken(user);
                var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
                if ((DateTime.Now - expiryDate).TotalSeconds > 0)
                {

                    context.Request.Headers.Authorization = null;
                    context.Request.RequestUri = null;
                    context.Request.RequestUri = new Uri("/Login");
                }
                else
                {
                    var authorize = new AuthenticationHeaderValue("token", newToken);
                    context.Request.Headers.Authorization = authorize;
                    context.ActionContext.Request.Headers.Authorization = authorization;
                }
                Challenge(context);

            }
        }
        else
        {
            var req = context.Request.RequestUri;
            var url = context.Request.RequestUri = new Uri($"http://{req.Host}:{req.Port}/api/Security/login");
            context.Request.RequestUri = url;
            context.Request.Headers.Authorization = null;
            context.Result= new AuthenticationFailureResult(string.Empty, new HttpRequestMessage());
        }


        return Task.FromResult(0);

    }

1 个答案:

答案 0 :(得分:-1)

首先:JWT只是客户端可以操作的客户端令牌。这里没有绝对的安全性。 JWT由对称密钥保护,但本身不能失效。有效令牌在过期之前一直保持有效。 JWT的一个缺陷(如@TimBiegeleisen在评论中指出的那样)是,令牌本身不能轻易失效。

如果用户工作时间太长并自动注销,则您的JWT已过期,一切都很好。没有麻烦,因为它会自然耗尽,您无需采取任何行动。

要使令牌无效,您需要使用Expires = now.AddMinutes(-1)创建一个新令牌。这样,下次下次检查JWT时,它就会过期。

只有在将JWT列入黑名单或永久维护某种其他服务器端会话的情况下,用户才可以保存JWT并在注销后仍使用它的情况(这不适用于无状态Web服务)。

编辑:删除了错误的信息,并添加了使JWT(黑名单)无效的其他方法