JWT Go/Golang base64 编码 payload 产生不同的结果
JWT Go/Golang base64 encoding payload yields different results
我有以下 JWT 负载:
{"exp": 4724377561}
(100 年后的某个日期)
在 Go 中对其进行编码会产生 ewogICAiZXhwIjogNDcyNDM3NzU2MQp9
然而,使用 jwt.io 它被编码为 eyJleHAiOjQ3MjQzNzc1NjF9
,这在签名时会产生不同的签名。我在测试装置中使用 jwt.io 的签名。
我不想使用第 3 方 JWT 工具,以保持我的依赖性。
我怀疑某种字符编码是这里的问题。
为了保持测试的可读性,我在固定装置中使用纯文本 JSON。
我使用测试夹具的方式如下(省略其他测试用例):
import (
"encoding/base64"
"reflect"
"testing"
)
var testData = []struct {
name string
header string
payload string
signature string
pass bool
errorType reflect.Type
}{{
name: "Succeed if token not expired",
header: `{"typ":"JWT","alg":"HS256"}`,
payload: `{"exp": 4724377561}`,
signature: "JHtMKvPSMa5BD22BsbxiP1-ELRh1XkIKkarRSev0ZjU",
pass: true,
}}
func TestParseJwt(t *testing.T) {
HmacSecret = []byte("My super secret key")
for _, tst := range testData {
jwt64 :=
base64.RawURLEncoding.EncodeToString([]byte(tst.header)) + "." +
base64.RawURLEncoding.EncodeToString([]byte(tst.payload)) + "." +
tst.signature
_, err := ParseJwt(jwt64)
if tst.pass {
if err != nil {
t.Fatal(tst.name, err)
}
} else {
// If an error was expected to be thrown, assert that it is the correct one.
if reflect.TypeOf(err).String() != tst.errorType.String() {
t.Fatal(tst.name, err)
}
}
}
}
差异仅来自库 "compacting" 和 JSON 在应用 Base64 编码之前。
看这个例子:
ss := []string{
`{"exp": 4724377561}`,
`{"exp":4724377561}`,
}
for _, s := range ss {
fmt.Println(base64.RawURLEncoding.EncodeToString([]byte(s)), s)
}
输出(在 Go Playground 上尝试):
eyJleHAiOiA0NzI0Mzc3NTYxfQ {"exp": 4724377561}
eyJleHAiOjQ3MjQzNzc1NjF9 {"exp":4724377561}
第二个输出符合您的预期。要在 Go 中删除无关紧要的空格,请使用 json.Compact.
转换回 https://www.base64decode.org/ 上的编码字符串会告诉您发生了什么:
ewogICAiZXhwIjogNDcyNDM3NzU2MQp9
被解码为
{
"exp": 4724377561
}
而 jwt.io 字符串被解码为
{"exp":4724377561}
所以 https://jwt.io 在编码前删除所有空格和换行符。
我有以下 JWT 负载:
{"exp": 4724377561}
(100 年后的某个日期)
在 Go 中对其进行编码会产生 ewogICAiZXhwIjogNDcyNDM3NzU2MQp9
然而,使用 jwt.io 它被编码为 eyJleHAiOjQ3MjQzNzc1NjF9
,这在签名时会产生不同的签名。我在测试装置中使用 jwt.io 的签名。
我不想使用第 3 方 JWT 工具,以保持我的依赖性。 我怀疑某种字符编码是这里的问题。
为了保持测试的可读性,我在固定装置中使用纯文本 JSON。
我使用测试夹具的方式如下(省略其他测试用例):
import (
"encoding/base64"
"reflect"
"testing"
)
var testData = []struct {
name string
header string
payload string
signature string
pass bool
errorType reflect.Type
}{{
name: "Succeed if token not expired",
header: `{"typ":"JWT","alg":"HS256"}`,
payload: `{"exp": 4724377561}`,
signature: "JHtMKvPSMa5BD22BsbxiP1-ELRh1XkIKkarRSev0ZjU",
pass: true,
}}
func TestParseJwt(t *testing.T) {
HmacSecret = []byte("My super secret key")
for _, tst := range testData {
jwt64 :=
base64.RawURLEncoding.EncodeToString([]byte(tst.header)) + "." +
base64.RawURLEncoding.EncodeToString([]byte(tst.payload)) + "." +
tst.signature
_, err := ParseJwt(jwt64)
if tst.pass {
if err != nil {
t.Fatal(tst.name, err)
}
} else {
// If an error was expected to be thrown, assert that it is the correct one.
if reflect.TypeOf(err).String() != tst.errorType.String() {
t.Fatal(tst.name, err)
}
}
}
}
差异仅来自库 "compacting" 和 JSON 在应用 Base64 编码之前。
看这个例子:
ss := []string{
`{"exp": 4724377561}`,
`{"exp":4724377561}`,
}
for _, s := range ss {
fmt.Println(base64.RawURLEncoding.EncodeToString([]byte(s)), s)
}
输出(在 Go Playground 上尝试):
eyJleHAiOiA0NzI0Mzc3NTYxfQ {"exp": 4724377561}
eyJleHAiOjQ3MjQzNzc1NjF9 {"exp":4724377561}
第二个输出符合您的预期。要在 Go 中删除无关紧要的空格,请使用 json.Compact.
转换回 https://www.base64decode.org/ 上的编码字符串会告诉您发生了什么:
ewogICAiZXhwIjogNDcyNDM3NzU2MQp9
被解码为
{
"exp": 4724377561
}
而 jwt.io 字符串被解码为
{"exp":4724377561}
所以 https://jwt.io 在编码前删除所有空格和换行符。