我试图用以下功能编写简单的JWT实现:

  • 使用HMAC生成令牌
  • 验证令牌(如果签名正确或exp未超时)
  • 解码 token 并获取索赔

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

到目前为止,我找到了这篇文章how to build an authentication microservice in golang from scratch.其中一章专门介绍JWT从头开始的实现.我使用了go generate token,但是当我在https://jwt.io中粘贴token时,我得到了无效的签名和以下警告:

我粘贴的令牌如下所示:

我的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编码

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

你可以看到StdEncodingRawStdEncodingRawURLEncoding之间的差异,在这个短途操场example.

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

Go相关问答推荐

Go 1.22 http mux:在同一路径上提供一个手柄和一个FS

Go编译器标志-修剪路径不完全工作

如何描述OpenAPI规范中围棋的数据类型.JSON?

有没有办法通过Go cmdline或IDE(IntelliJ)找出我的 struct 实现了什么接口?

Go Regexp:匹配完整的单词或子字符串,或者根本不匹配

如何在v2 Go SDK中使用KeyConditionExpression查询AWS DynamoDb?

使用!NOT运算符的Golang文本/模板多个条件

golang regex基于关键字拆分字符串

go mod tidy会自动升级go.mod中的go版本吗?

我怎样才能改进这个嵌套逻辑以使其正常工作并提高性能

运行 Docker 容器 - X11 无法打开显示:0

Golang Fiber Render - 将数据发送到多个布局

用接口来模拟amqp091go的困难

如何从 Go Lambda 函数返回 HTML?

go version -m 输出中的箭头符号=>是什么意思?

如何使用 Docker 引擎 SDK 和 Golang 运行 docker 挂载卷

Gorm 在保存/创建时序列化 struct

没有堆栈跟踪的 go 程序崩溃是什么意思?

使用 oklog/run 来自 Go 编译器的错误(无值)用作值

try 创建新的 etcdv3 客户端时出现pc error: code = Unavailable desc = error reading from server: EOF