我试图用以下功能编写简单的JWT实现:
- 使用
HMAC
生成令牌 - 验证令牌(如果签名正确或exp未超时)
- 解码 token 并获取索赔
从头开始,更好地理解它是如何深入工作的.
到目前为止,我找到了这篇文章how to build an authentication microservice in golang from scratch.其中一章专门介绍JWT从头开始的实现.我使用了go generate token,但是当我在https://jwt.io中粘贴token时,我得到了无效的签名和以下警告:
-
Warning:看起来您的JWT签名没有使用base64url(https://tools.ietforg/html/rfc4648#section-5)正确编码.注意,根据https://tools.ietf.org/html/rfc7515#section-2,必须省略填充("=")
-
Warning:看起来您的JWT头没有使用base64url(https://tools.ietforg/html/rfc4648#section-5)正确编码.注意,根据https://tools.ietf.org/html/rfc7515#section-2,必须省略填充("=")
-
Warning:看起来您的JWT负载没有使用base64url(https://tools.ietforg/html/rfc4648#section-5)正确编码.注意,根据https://tools.ietf.org/html/rfc7515#section-2,必须省略填充("=")
我粘贴的令牌如下所示:
我的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
来实现,但是任何其他语言的示例都非常受欢迎.