使用 Base64 解码 JWT Playload 数据不完整
微信公众号
·
2020-06-08
·
via 陈少文的网站
陈少文的网站 Posts 使用 Base64 解码 JWT Playload 数据不完整 Please enable Javascript to view the contents
在对 JWT 进行 Base64 解码时,发现 JSON 数据不完整。本文主要介绍相关知识点并解决这个问题。
1. JWT 简介 JWT 通过在 Header 中设置 Authorization: Bearer <token> 进行认证的传递。
JWT Token 是一个 . 连接的 Base64 编码字符串,类似这样 Header.Payload.Signature ,有三部分组成:
1
2
3
4
{
"alg" : "HS256" ,
"typ" : "JWT"
}
Payload ,负载信息,通常是 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)等 1
2
3
4
5
{
"sub" : "1234567890" ,
"name" : "John Doe" ,
"admin" : true
}
Signature ,对 Base64 编码的 Header 和 Playload 进行签名,防止信息被篡改。 1
2
3
4
5
HMACSHA 256 (
base 64 UrlEncode(header) + "." +
base 64 UrlEncode(payload),
your -256 -bit-secret
)
jwt.io 提供了一个在线解析 Token 的工具。
2. Base64 解码 “encoding/base64” 提供了四种编码和解码的方法:
StdEncoding , 常规编码,不足 3 倍时,使用 = 补齐 URLEncoding , URL safe 编码,替换掉字符串中的特殊字符 +/ 转化成 -_ RawStdEncoding , 常规编码,末尾不补 = RawURLEncoding , URL safe 编码,末尾不补 = 下面,通过具体代码,看看它们之间的差别。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main
import (
"encoding/base64"
"fmt"
)
func coding ( msg [] byte ){
fmt . Println ( "Input :" , string ( msg ))
encoded := base64 . StdEncoding . EncodeToString ( msg )
fmt . Println ( "StdEncoding :" , encoded )
decoded , _ := base64 . StdEncoding . DecodeString ( encoded )
fmt . Println ( "StdEncoding :" , string ( decoded ))
encoded = base64 . URLEncoding . EncodeToString ( msg )
fmt . Println ( "URLEncoding :" , encoded )
decoded , _ = base64 . URLEncoding . DecodeString ( encoded )
fmt . Println ( "URLEncoding :" , string ( decoded ))
encoded = base64 . RawStdEncoding . EncodeToString ( msg )
fmt . Println ( "RawStdEncoding :" , encoded )
decoded , _ = base64 . RawStdEncoding . DecodeString ( encoded )
fmt . Println ( "RawStdEncoding :" , string ( decoded ))
encoded = base64 . RawURLEncoding . EncodeToString ( msg )
fmt . Println ( "RawURLEncoding :" , encoded )
decoded , _ = base64 . RawURLEncoding . DecodeString ( encoded )
fmt . Println ( "RawURLEncoding :" , string ( decoded ))
}
func main () {
// 补齐
coding ([] byte ( "https://www.chenshaowen.com/" ))
// URL Safe 编码
coding ([] byte ( "abc123!?$*&()'-=@~" ))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Input : https://www.chenshaowen.com/
StdEncoding : aHR0cHM6Ly93d3cuY2hlbnNoYW93ZW4uY29tLw==
StdEncoding : https://www.chenshaowen.com/
URLEncoding : aHR0cHM6Ly93d3cuY2hlbnNoYW93ZW4uY29tLw==
URLEncoding : https://www.chenshaowen.com/
RawStdEncoding : aHR0cHM6Ly93d3cuY2hlbnNoYW93ZW4uY29tLw
RawStdEncoding : https://www.chenshaowen.com/
RawURLEncoding : aHR0cHM6Ly93d3cuY2hlbnNoYW93ZW4uY29tLw
RawURLEncoding : https://www.chenshaowen.com/
Input : abc123!?$*&()'-=@~
StdEncoding : YWJjMTIzIT8kKiYoKSctPUB+
StdEncoding : abc123!?$*&()'-=@~
URLEncoding : YWJjMTIzIT8kKiYoKSctPUB-
URLEncoding : abc123!?$*&()'-=@~
RawStdEncoding : YWJjMTIzIT8kKiYoKSctPUB+
RawStdEncoding : abc123!?$*&()'-=@~
RawURLEncoding : YWJjMTIzIT8kKiYoKSctPUB-
RawURLEncoding : abc123!?$*&()'-=@~
从输出的结果来看:
Stdxxx 会对 Base64 编码执行补齐 URLxxx 会对 Base64 编码进行转码 Base64 是公开的标准编码规则,但不同的库实现时,暴露出来的接口会有差异,使用正确的接口才能获得预期的结果。
3. JWT Playload 少了一部分 下面这段代码截取了 Playload 部分进行解析:
1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"encoding/base64"
"fmt"
)
func main () {
decoded := "eyJ1c2VybmFtZSI6ImFkbWluIiwidWlkIjoiYjhiZTZlZGQtMmM5Mi00NTM1LTliMmEtZGY2MzI2NDc0NDU4IiwiaWF0IjoxNTkxMzU0MDEwLCJpc3MiOiJrdWJlc3BoZXJlIiwibmJmIjoxNTkxMzU0MDEwfQ"
encoded , _ := base64 . StdEncoding . DecodeString ( decoded )
fmt . Println ( string ( encoded ))
}
得到结果:
1
{ "username" :"admin" ,"uid" :"b8be6edd-2c92-4535-9b2a-df6326474458" ,"iat" :1591354010,"iss" :"" ,"nbf" :1591354010
发现,这并不是一个完整的 Json 对象。在 dgrijalva/jwt-go 库中,可以看到 EncodeSegment 函数的实现:
1
2
3
4
// Encode JWT specific base64url encoding with padding stripped
func EncodeSegment ( seg [] byte ) string {
return strings . TrimRight ( base64 . URLEncoding . EncodeToString ( seg ), "=" )
}
显然,dgrijalva/jwt-go 使用的是 RawURLEncoding 的方式进行编码。
调整之后,执行下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"encoding/base64"
"fmt"
)
func main () {
decoded := "eyJ1c2VybmFtZSI6ImFkbWluIiwidWlkIjoiYjhiZTZlZGQtMmM5Mi00NTM1LTliMmEtZGY2MzI2NDc0NDU4IiwiaWF0IjoxNTkxMzU0MDEwLCJpc3MiOiJrdWJlc3BoZXJlIiwibmJmIjoxNTkxMzU0MDEwfQ"
encoded , _ := base64 . RawURLEncoding . DecodeString ( decoded )
fmt . Println ( string ( encoded ))
}
得到正确结果:
1
{ "username" :"admin" ,"uid" :"b8be6edd-2c92-4535-9b2a-df6326474458" ,"iat" :1591354010,"iss" :"" ,"nbf" :1591354010}
另外一种方式是,使用 dgrijalva/jwt-go 内置的解析器,提供完整的 JWT Token 进行解析。可以看看下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"github.com/dgrijalva/jwt-go"
"fmt"
)
func main () {
decoded := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidWlkIjoiYjhiZTZlZGQtMmM5Mi00NTM1LTliMmEtZGY2MzI2NDc0NDU4IiwiaWF0IjoxNTkxMzU0MDEwLCJpc3MiOiJrdWJlc3BoZXJlIiwibmJmIjoxNTkxMzU0MDEwfQ.psKkj8vYWm9Crf9jnbB_PNestLNksaS9vuMvQI3C-dU"
type Claims struct {
Username string `json:"username"`
UID string `json:"uid"`
jwt . StandardClaims
}
claim := Claims {}
parser := jwt . Parser {}
parser . ParseUnverified ( decoded , & claim )
fmt . Println ( claim . Username )
}
得到预期结果:
admin
4. 参考
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。