登录
首页 >  Golang >  Go问答

使用正确的 JWT 编码

来源:stackoverflow

时间:2024-03-15 09:24:28 137浏览 收藏

Golang小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《使用正确的 JWT 编码》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!


问题内容

我正在尝试编写具有以下功能的简单 jwt 实现:

  • 使用 hmac 生成令牌
  • 验证令牌(如果签名正确或 exp 未超时)
  • 解码令牌并获取声明

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

到目前为止,我找到了这篇文章如何从头开始在 golang 中构建身份验证微服务。其中有一章致力于从头开始实现 jwt。我用它去生成令牌,但是当我将令牌粘贴到 https://jwt.io 时,我收到了无效签名和以下警告:

  • 警告:您的 jwt 签名似乎未使用 base64url (https://tools.ietforg/html/rfc4648#section-5) 正确编码。请注意,必须根据 https://tools.ietf.org/html/rfc7515#section-2 省略填充(“=”)

  • 警告:您的 jwt 标头似乎未使用 base64url (https://tools.ietforg/html/rfc4648#section-5) 正确编码。请注意,必须根据 https://tools.ietf.org/html/rfc7515#section-2 省略填充(“=”)

  • 警告:您的 jwt 负载似乎未使用 base64url (https://tools.ietforg/html/rfc4648#section-5) 正确编码。请注意,必须根据 https://tools.ietf.org/html/rfc7515#section-2 省略填充(“=”)

我粘贴的令牌如下所示: eyaiywxnijogikhtmju2iiwginr5cci6icjkv1qiih0=.eyjhdwqioijmc​​m9udgvuzc5rbm93c2vhcmnolm1siiwizxhwijoimty1mtiymjcymyisimlzcyi6imtub3dzzwfyy2gubwwifq ==.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 要求删除所有填充 = 字符:

使用 URL 和文件名安全字符集的 Base64 编码 在 RFC 4648 [RFC4648] 第 5 节中定义,所有尾随“=” 省略字符(第 3.2 节允许)并且没有 包含任何换行符、空格或其他附加内容 字符。

您可以使用 base64.RawURLEncoding (它会创建不带填充的 Base64Url 编码)来代替 base64.StdEncoding

您可以在这个简短的 Go 演示 example 中看到 StdEncodingRawStdEncodingand RawURLEncoding 之间的差异。

另外,如果不是为了学习练习,我强烈建议使用JWT library

本篇关于《使用正确的 JWT 编码》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>