解码JWT错误:无法将有效载荷解码为Base64Url编码的字符串

3

我有一个jwt令牌'eyJhbGciOiJIUzI1NiJ9.YUExIQ.srnc87a8Se8arhCopLBpgxEvILA2AZxOB8BIrFDHKL4',是在node中编码的,现在我想在c#中解码它,但是我得到了一个错误,说负载有问题。

    public void Consume(BasicDeliverEventArgs msg)
    {
        var message = Encoding.UTF8.GetString(msg.Body);
        var user = JsonConvert.DeserializeObject<User>(message);

        var handler = new JwtSecurityTokenHandler();

        try
        {    
            var x = handler.ReadToken(user.Password) as JwtSecurityToken;
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }



        _repo.UpdateUser(user);
    }

返回错误:无法将有效载荷“YUExIQ”解码为Base64Url编码字符串

我不确定是否有地方可以放置用于解码的密钥,或者是否需要将有效载荷转换为base64格式,或者我是否遗漏了某些步骤。提前致谢。

3个回答

2
由于您询问了使用Base64进行解码的问题,我将重点关注这个问题,而不是在.NET中使用JWT的语义。这应该会让JWT令牌变得更加清晰易懂。
请注意,您上面的JWT字符串只有第一个部分有效-它转换为{"alg":"HS256"},而第二个部分转换为aA1!-因此,为了举例说明,我将使用http://jwt.io中的默认示例值:
var jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";

根据JWT.io的介绍,JWT令牌只有三个部分:

  • 头部
  • 有效载荷
  • 签名

假设令牌未加密,则前两个部分可以使用普通的Base64解码(一旦将长度填充为四的倍数,如@Jacob上面提到的)。最后一个部分只是一个加密签名;它不包含任何有意义的编码信息可供解码。

因此,显示解码后的JWT内容的简单代码片段可能如下所示:

var decoded = jwt.Split('.')
  .Take(2)
  .Select(x => 
    Encoding.UTF8.GetString(
      Convert.FromBase64String(
        x.PadRight(x.Length + (x.Length % 4), '='))))
.Aggregate((s1, s2) => s1 + Environment.NewLine + s2);

Console.WriteLine(decoded);

/* Prints:
   {
     "alg": "HS256",
     "typ": "JWT"
   }
   {
     "sub": "1234567890",
     "name": "John Doe",
     "admin": true
   }
*/

一个将所有lambda分开的更简单的例子可能如下所示:
/// <summary>
/// A function to make the Base64 decoding a little less
/// verbose below:
/// </summary>
string Base64Decode(string value)
{
    return Encoding.Default.GetString(Convert.FromBase64String(value)); 
}

// Get the first two segments into an enumerable:
var pieces = jwt.Split('.').Take(2);
// Pad them with equals signs to a length that is a multiple of four:
var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));
// Base64 decode the pieces:
var decodedPieces = paddedPieces.Select(x => Base64Decode(x));
// Join it all back into one string with .Aggregate:
Console.WriteLine(decodedPieces.Aggregate((s1, s2) => s1 + Environment.NewLine + s2));

这里的结果与上面的相同。
当然,如果您打算执行有意义的JWT操作,如签名和验证,您应该使用System.IdentityModel.Tokens.Jwt中的JwtSecurityToken类。但是,如果您只需要查找非加密令牌包含的信息,那么就像上面一样简单。

我认为这个答案还不错,但是它忘了提到 base64url 用于JWT。因此需要将字符串中的“-”替换为“+”,将“_”替换为“/”。 - Alexander Farber

2

我无法添加评论,只想在@paul-smith的答案上补充一点。

他的答案有效,但是在将字符串填充到4的倍数的逻辑中存在一个小错误。

不是使用以下代码:

var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));

应该是...

var paddedPieces = pieces.Select(x => x.PadRight(x.Length + ((x.Length % 4 != 0) ? (4 - x.Length % 4) : 0), '='));

你需要检查"x.Length % 4 != 0",否则在字符串已经是4的倍数时,它可能会在末尾添加"====",从而破坏Base64解码。但真正的问题是应该是(4 - x.Length % 4)而不是(x.Length % 4) 例如:
如果字符串长度为7个字符...
7 + (7 % 4) = 10
7 + (4 - (7 % 4) = 8

0

使用JWT时,您可以省略填充字节。但是.NET需要在末尾添加填充=字符,以使字符数成为4的倍数。YUExIQ==应该可以解码。


你需要分别为每个点分隔的部分添加填充。=字符在JWT中无效,但在解析时,你需要添加它们。 - Jacob
我不确定你的意思,因为ReadToken只接受一个字符串参数。你是想让我拆分(.)并添加=填充,然后将它们连接在一起吗? - bradley
我认为错误在于你调用的方法,而不是你发布的代码。这是第三方库吗? - Jacob

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