正确编码 JWT

Encode JWT properly

我正在尝试编写具有这些功能的简单 JWT 实现:

从头开始,以便更好地理解它是如何深入工作的。

到目前为止我找到这篇文章how to build an authentication microservice in golang from scratch. One chapter is dedicated to implementation JWT from scratch. I used it go generate token, however when I paste token in https://jwt.io我有无效的签名和以下警告:

我粘贴的令牌如下所示: eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJKV1QiIH0=.eyJhdWQiOiJmcm9udGVuZC5rbm93c2VhcmNoLm1sIiwiZXhwIjoiMTY1MTIyMjcyMyIsImlzcyI6Imtub3dzZWFyY2gubWwifQ==.SqCW8Hxakzck9Puzl0BEOkREPDyl38g2Fd4KFaDazV4=

我的 JWT 代码实现:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "strings"
    "time"
)

func GenerateToken(header string, payload map[string]string, secret string) (string, error) {
    h := hmac.New(sha256.New, []byte(secret))
    header64 := base64.StdEncoding.EncodeToString([]byte(header))

    payloadstr, err := json.Marshal(payload)
    if err != nil {
        return "", err
    }
    payload64 := base64.StdEncoding.EncodeToString(payloadstr)

    message := header64 + "." + payload64

    unsignedStr := header + string(payloadstr)

    h.Write([]byte(unsignedStr))
    signature := base64.StdEncoding.EncodeToString(h.Sum(nil))

    tokenStr := message + "." + signature
    return tokenStr, nil
}

func ValidateToken(token string, secret string) (bool, error) {
    splitToken := strings.Split(token, ".")

    if len(splitToken) != 3 {
        return false, nil
    }

    header, err := base64.StdEncoding.DecodeString(splitToken[0])
    if err != nil {
        return false, err
    }
    payload, err := base64.StdEncoding.DecodeString(splitToken[1])
    if err != nil {
        return false, err
    }

    unsignedStr := string(header) + string(payload)
    h := hmac.New(sha256.New, []byte(secret))
    h.Write([]byte(unsignedStr))

    signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
    fmt.Println(signature)

    if signature != splitToken[2] {
        return false, nil
    }

    return true, nil
}

func main() {
    claimsMap := map[string]string{
        "aud": "frontend.knowsearch.ml",
        "iss": "knowsearch.ml",
        "exp": fmt.Sprint(time.Now().Add(time.Second * 2).Unix()),
    }
    secret := "Secure_Random_String"
    header := `{ "alg": "HS256", "typ": "JWT" }`

    tokenString, err := GenerateToken(header, claimsMap, secret)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println("token: ", tokenString)

    isValid, _ := ValidateToken(tokenString, secret)
    fmt.Println("is token valid: ", isValid)

    duration := time.Second * 4
    time.Sleep(duration)

    isValid, _ = ValidateToken(tokenString, secret)
    fmt.Println("is token valid: ", isValid)

}

上面的实现有什么问题,如何解决它并消除警告?

我决定使用 Golang 来实现,但是非常感谢任何其他语言的示例。

JWT specification 要求删除所有填充 = 个字符:

Base64 encoding using the URL- and filename-safe character set defined in Section 5 of RFC 4648 [RFC4648], with all trailing '=' characters omitted (as permitted by Section 3.2) and without the inclusion of any line breaks, whitespace, or other additional characters.

您可以使用 base64.RawURLEncoding ,它创建无填充的 Base64Url 编码,而不是 base64.StdEncoding

您可以在这个简短的 Go Playground example.

中看到 StdEncodingRawStdEncodingRawURLEncoding 之间的区别

此外,如果不是为了学习锻炼,我强烈建议使用JWT library