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,提问作者黃柏瑋
相关产品推荐
相关产品推荐

