You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言jwt.ParseWithClaims解析令牌报错:令牌格式无效

Go语言JWT验证解析失败问题

报错信息:

token is malformed: could not base64 decode header: illegal base64 data at input byte 0

示例令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjaGlycHkiLCJzdWIiOiJhMTIzZmRlZC00MDlhLTQ2YjAtYTI4Mi0wYzlhNDdhYjNmZWIiLCJleHAiOjE3Mjc4NjMwMTYsImlhdCI6MTcyNzg1OTQxNn0.ir-ZGH_LluAHDLWEJog4aJ681ByidoYyyeC5_Gb1HbI

令牌密钥:

gm0Bx90rG/8Rup6peskqYWABEqjZyV6WrFKXraMG10y8X3mLgghbORBz4ZYQQhN4lXTP3ByOHAPmn7nW4Teeag

相关代码

ValidateJWT验证函数

func ValidateJWT(tokenString, tokenSecret string) (uuid.UUID, error) {
    token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
       
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            log.Printf("unexpected signing method: %v", token.Header["alg"])
            return nil, errors.New("unexpected signing method")
        }
        return []byte(tokenSecret), nil
    })

    if err != nil {
        log.Printf("Error parsing token: %v", err)
        return uuid.Nil, err 
    }

    if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
        userID, err := uuid.Parse(claims.Subject)
        if err != nil {
            log.Printf("Error parsing user ID from claims: %v", err)
            return uuid.Nil, err
        }
        return userID, nil
    }

    log.Println("could not extract claims from token")
    return uuid.Nil, errors.New("could not extract claims")
}

SaveHandler HTTP处理函数

func (cfg *apiConfig) SaveHandler(w http.ResponseWriter, r *http.Request) {
    type BodyPayload struct {
        Body string    `json:"body"`
        Id uuid.UUID    `json:"id"`
        
    }

    req := BodyPayload{}
    err := json.NewDecoder(r.Body).Decode(&req)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(map[string]string{"error": "something went wrong"})
        return
    }

    
    Jwt, err := auth.GetBearerToken(r.Header)
    if err != nil {
        http.Error(w, "Could not get bearer token", http.StatusInternalServerError)
        return
    }

    // Validate the JWT
    JWTid, err := auth.ValidateJWT(Jwt, cfg.Secret)
    if err != nil {
        
        w.WriteHeader(http.StatusUnauthorized)  //error occurs here
        json.NewEncoder(w).Encode(map[string]string{"error": "error parsing token"})
        return
    }

    if JWTid != req.Id{
        w.WriteHeader(http.StatusUnauthorized)
        json.NewEncoder(w).Encode(map[string]string{"error": "invalid or expired token"})
        return
    }
    
    if len(req.Body) > 140 {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(map[string]string{"error": "chirp too long"})
        return
    }

    wordsToCensor := []string{"kerfuffle", "sharbert", "fornax"}
    censored := censorText(req.Body, wordsToCensor)

    type ResponseBody struct {
        Id        uuid.UUID    `json:"id"`
        CreatedAt time.Time     `json:"created_at"`
        UpdatedAt time.Time     `json:"updated_at"`
        Body      string       `json:"body"`
        UserID    uuid.UUID    `json:"user_id"`
    }

    // Here, you can use the validated JWTid to associate the chirp with the user
    user, err := cfg.dbQueries.SaveUser(r.Context(), database.SaveUserParams{
        Body:   censored,
        UserID: req.Id, 
    })

    if err != nil {
        log.Printf("Error creating user: %v", err)
        http.Error(w, "Could not create user", http.StatusInternalServerError)
        return
    }

    resp := ResponseBody{
        Id:        user.ID,
        CreatedAt: user.CreatedAt,
        UpdatedAt: user.UpdatedAt,
        Body:      censored,
        UserID:    user.UserID,
    }

    w.WriteHeader(http.StatusCreated)
    if err := json.NewEncoder(w).Encode(resp); err != nil {
        http.Error(w, "Failed to encode response", http.StatusInternalServerError)
    }
}

GetBearerToken令牌提取函数

func GetBearerToken(headers http.Header) (string, error) {
    // Get the Authorization header
    tokenString := headers.Get("Authorization")

    // Check if the Authorization header is present
    if tokenString == "" {
        return "", errors.New("authorization header missing")
    }

    // Check if it starts with "Bearer "
    if !strings.HasPrefix(tokenString, "Bearer ") {
        return "", errors.New("invalid authorization header format")
    }

    // Remove the "Bearer " prefix and trim any extra whitespace
    token := strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer "))

    // Return the token and no error
    return token, nil
}

排查情况

已确认传入ValidateJWT函数的令牌格式显示正常,但调用jwt.ParseWithClaims时仍返回上述解析错误。

解决建议

  • 排查令牌隐藏字符:即使日志显示格式正常,也可能存在不可见字符(如UTF-8 BOM、换行符、空格)。可在ValidateJWT开头加入调试代码,输出令牌字节长度和每个字节的ASCII值:
    log.Printf("Token length: %d", len(tokenString))
    for i, b := range tokenString {
        log.Printf("Byte %d: %d (%c)", i, b, b)
    }
    
  • 验证密钥一致性:确认生成令牌和验证时使用的密钥完全一致,密钥中的特殊字符(如斜杠)无需额外转义,直接转为[]byte即可,但要注意是否存在编码差异。
  • 检查jwt库版本:不同版本的jwt-go解析逻辑可能有差异,确保生成和验证使用同一版本,或升级到最新稳定版本。
  • 手动验证令牌头部:用Base64 URL解码工具手动解析令牌第一部分(头部),示例令牌头部解码后应为{"alg":"HS256","typ":"JWT"},若解码失败则说明令牌本身存在问题。
  • 确认令牌提取逻辑:在SaveHandler中打印提取后的令牌字符串,确保和示例令牌完全一致,无截断或多余字符。

内容的提问来源于stack exchange,提问作者黃柏瑋

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 17:05:59