如何验证JWT令牌

38

我正在尝试使用JWT令牌。我成功生成了有效的JWTTokenString并在JWT调试器上验证了它,但是我无法在.Net中验证该令牌。以下是我目前拥有的代码:

class Program {

    static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

    static void Main(string[] args) {
        var stringToken = GenerateToken();
        ValidateToken(stringToken);
    }

    private static string GenerateToken() {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));

        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var header = new JwtHeader(credentials);

        var payload = new JwtPayload {
           { "some ", "hello "},
           { "scope", "world"},
        };

        var secToken = new JwtSecurityToken(header, payload);
        var handler = new JwtSecurityTokenHandler();

        return handler.WriteToken(secToken);

    }

    private static bool ValidateToken(string authToken) {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = GetValidationParameters();

        SecurityToken validatedToken;
        IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
        Thread.CurrentPrincipal = principal;
        return true;
    }

    private static TokenValidationParameters GetValidationParameters() {
        return new TokenValidationParameters() {
            //NOT A CLUE WHAT TO PLACE HERE
        };
    }
}
我只希望有一个函数,该函数接收一个令牌并根据其是否有效返回true或false。从我所了解的研究中,人们使用来分配验证密钥。但是当我尝试使用它时,似乎不存在。有谁能帮我验证令牌吗?
4个回答

67

您必须使用与生成令牌时相同的密钥来验证令牌。此外,您需要禁用一些验证,如过期时间、发行人和受众,因为您生成的令牌没有这些信息(或者您可以添加这些信息)。这里是一个可工作的示例:

class Program
{
    static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

    static void Main(string[] args)
    {
        var stringToken = GenerateToken();
        ValidateToken(stringToken);
    }

    private static string GenerateToken()
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var secToken = new JwtSecurityToken(
            signingCredentials: credentials,
            issuer: "Sample",
            audience: "Sample",
            claims: new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, "meziantou")
            },
            expires: DateTime.UtcNow.AddDays(1));

        var handler = new JwtSecurityTokenHandler();
        return handler.WriteToken(secToken);
    }

    private static bool ValidateToken(string authToken)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = GetValidationParameters();

        SecurityToken validatedToken;
        IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
        return true;
    }

    private static TokenValidationParameters GetValidationParameters()
    {
        return new TokenValidationParameters()
        {
            ValidateLifetime = false, // Because there is no expiration in the generated token
            ValidateAudience = false, // Because there is no audiance in the generated token
            ValidateIssuer = false,   // Because there is no issuer in the generated token
            ValidIssuer = "Sample",
            ValidAudience = "Sample",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // The same key as the one that generate the token
        };
    }
}

5
为什么 ValidateToken 总是返回 true? - Casey Crookston
3
@CaseyCookston 这有点奇怪,但如果Token无效,ValidateToken会抛出一个异常。因此,如果代码执行到那一行,那么Token就是有效的。也许应该将其改为返回void,并将其重命名为AssertValidToken(...)? - DanTheMan
我正在尝试实现苹果登录,到了从苹果获取JWT并想要验证它的点。我找到了这篇文章,但有些地方我不理解。在推荐的解决方案中,您正在构建一个JWT令牌以便稍后调用ValidateToken来验证该令牌,但为什么不直接对由Apple返回的JWT调用ValidateToken呢?另一方面,在推荐的解决方案中,变量key是苹果的公钥,对吗? - Enric
如果validateToken()总是返回true,你不需要返回任何东西。你只需要调用validateToken()。你应该编写Javadocs来澄清,如果令牌无效,该方法会抛出异常。 - MiguelMunoz
尝试 { IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken); } catch (Exception e) { return false; }它的工作文件 - Kamruzzaman
显示剩余2条评论

2

在我的情况下,我只想验证签名是否正确。很可能您会选择使用@meziantou的答案。但如果您只想验证消息是否未被篡改,这是一个例子。最后,由于只有我一个人生成这些令牌,并且我知道我将使用HmacSha256生成它们,因此我使用了这种方法。

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static readonly byte[] key = Encoding.UTF8.GetBytes("f645b33ef0d04cbe859777ac6f46226d");

    // use this algorithm for example to work
    static readonly string securityAlgorithm = SecurityAlgorithms.HmacSha256;

    static void Main()
    {
        var token = GenerateToken();
        var isTokenValid = IsJwtTokenValid(token);
        if (isTokenValid)
            Console.WriteLine(true);
    }

    /// <summary>
    ///     This method assumes token has been hashed using HMACSHA256 algorithm!
    /// </summary>
    private static bool IsJwtTokenValid(string token)
    {
        // example of token:
        //                  header                              payload                                      signature
        // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ.Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ

        // from JWT spec
        static string Base64UrlEncode(byte[] input)
        {
            var output = Convert.ToBase64String(input);
            output = output.Split('=')[0]; // Remove any trailing '='s
            output = output.Replace('+', '-'); // 62nd char of encoding
            output = output.Replace('/', '_'); // 63rd char of encoding
            return output;
        }

        try
        {
            // position of second period in order to split header+payload and signature
            int index = token.IndexOf('.', token.IndexOf('.') + 1);

            // Example: Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
            string signature = token[(index + 1)..];

            // Bytes of header + payload
            // In other words bytes of: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ
            byte[] bytesToSign = Encoding.UTF8.GetBytes(token[..index]);

            // compute hash
            var hash = new HMACSHA256(key).ComputeHash(bytesToSign);
            var computedSignature = Base64UrlEncode(hash);

            // make sure that signatures match
            return computedSignature.Length == signature.Length 
                && computedSignature.SequenceEqual(signature);
        }
        catch
        {
            return false;
        }
    }

    private static string GenerateToken()
    {
        var securityKey = new SymmetricSecurityKey(key);
        var credentials = new SigningCredentials(securityKey, securityAlgorithm);

        var secToken = new JwtSecurityToken(
            signingCredentials: credentials,
            claims: new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, "foo@gmail.com")
            },
            expires: DateTime.UtcNow.AddDays(1));

        var handler = new JwtSecurityTokenHandler();
        return handler.WriteToken(secToken);
    }
    
}

这对我来说解决了Salesforce自定义活动JWT令牌的问题。为了帮助任何试图在SMC上实现JWT的人,我留下了这个评论。 - nikolasd

2
在 Jwt 中间件类中验证令牌,该类在每个请求中调用 fire 方法以进行授权。
JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TokenValidationParameters _tokenValidationParams;
        public JwtMiddleware(RequestDelegate next, TokenValidationParameters 
        tokenValidationParams)
        {
            _next = next;
            _tokenValidationParams = tokenValidationParams;
        }

    

    public async Task Invoke(HttpContext context)
            {
            try{
                var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
    
                var jwtTokenHandler = new JwtSecurityTokenHandler();
                // Validation 1 - Validation JWT token format
                var tokenInVerification = jwtTokenHandler.ValidateToken(token, _tokenValidationParams, out var validatedToken);
    
                if (validatedToken is JwtSecurityToken jwtSecurityToken)
                {
                    var result = jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);
    
                    if (result == false)
                    {
                        Error Invalid = new Error()
                        {
                            Success = false,
                            Errors = "Token is Invalid"
                        };
    
                        context.Items["Error"] = Invalid;
                    }
                }
           }
           catch (Exception ex)
            {
                Error Invalid = new Error()
                {
                    Success = false,
                    Errors = "Token does not match or may expired."
                };
                context.Items["Error"] = Invalid ; // userService.GetById(userId);
            }
                await _next(context);
        }
    }

0
您可以使用以下代码在C#中验证JWT令牌。在我的配置中,我将过期时间确定为3天,并检查发行人受众
    var issuer; // The same issuer as the one that generate the token
    var audience; // The same audience as the one that generate the token
    var key; // The same key as the one that generate the token  

    public bool TokenKontrol(string authToken)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var validationParameters = GetValidationParameters();

            SecurityToken validatedToken;
            IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
            
            if (principal.Identity != null && principal.Identity.IsAuthenticated)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        catch (Exception ex)
        {
            return false;

        }
    }

    private TokenValidationParameters GetValidationParameters()
    {
      return new TokenValidationParameters()
        {
            ValidateLifetime = true,
            ValidateAudience = true,
            ValidateIssuer = true,
            ValidIssuer = issuer,
            ValidAudience = audience,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
        };
    }

请问您能提供令牌生成代码吗?因为验证“Issuer”取决于令牌首次生成的方式。 - Shahryar Saljoughi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接