繁体   English   中英

使用 C# 撤销 JWT

[英]Revoke JWT using C#

我有一个自定义Authorize class,当用户从服务器请求数据或任何内容时,我用它来使令牌无效

但每当令牌过期时,原则仍将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);

    }

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

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

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

用户可以保存 JWT 并在注销后使用它的情况只能通过将 JWT 列入黑名单或另外维护某种其他类型的服务器端会话来实现(这不适用于无状态 Web 服务)。

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

您可以简单地兑现您想要撤销的令牌,然后让您的身份验证部分将已撤销的请求与现金中的请求进行比较,并根据该请求拒绝请求

直到代币到期,现金应该知道代币的重新开采时间并兑现那么长时间

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM