{"record":{"id":"073188d4f26780cb","repo":"flipped-aurora/gin-vue-admin","slug":"token-073188","errorCode":null,"errorMessage":"这不是一个token","messagePattern":"这不是一个token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/jwt.go","lineNumber":21,"sourceCode":"import (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/flipped-aurora/gin-vue-admin/server/global\"\n\t\"github.com/flipped-aurora/gin-vue-admin/server/model/system/request\"\n\tjwt \"github.com/golang-jwt/jwt/v5\"\n)\n\ntype JWT struct {\n\tSigningKey []byte\n}\n\nvar (\n\tTokenValid            = errors.New(\"未知错误\")\n\tTokenExpired          = errors.New(\"token已过期\")\n\tTokenNotValidYet      = errors.New(\"token尚未激活\")\n\tTokenMalformed        = errors.New(\"这不是一个token\")\n\tTokenSignatureInvalid = errors.New(\"无效签名\")\n\tTokenInvalid          = errors.New(\"无法处理此token\")\n)\n\nfunc NewJWT() *JWT {\n\treturn &JWT{\n\t\t[]byte(global.GVA_CONFIG.JWT.SigningKey),\n\t}\n}\n\nfunc (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims {\n\tbf, _ := ParseDuration(global.GVA_CONFIG.JWT.BufferTime)\n\tep, _ := ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)\n\tclaims := request.CustomClaims{\n\t\tBaseClaims: baseClaims,\n\t\tBufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tAudience:  jwt.ClaimStrings{\"GVA\"},                   // 受众","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/jwt.go#L3-L39","documentation":"TokenMalformed is returned by ParseToken when the token string cannot even be parsed as a JWT — e.g. it is not three dot-separated base64 segments. It maps the jwt.ErrTokenMalformed / parse-failure case of the underlying jwt library.","triggerScenarios":"The x-token or Authorization header contains a truncated token, a JWT issued by a different format (e.g. a raw session id), a token with newlines/quotes copied from a terminal, or an empty/whitespace string that slips past other checks.","commonSituations":"Copy-paste truncation of the token; client sending a prefixed value like 'Bearer eyJ...' inside a header field expected to be the bare token (x-token); storage layer corrupting the token.","solutions":["Re-login to obtain a fresh, complete token and ensure it is copied in full.","Strip the 'Bearer ' prefix before assigning the token if sending via Authorization.","Log the token's shape (length, segment count) in a debug branch to spot truncation — never log the token itself.","Ensure the frontend stores the token unchanged (no extra quotes/newlines) when writing to localStorage/cookies."],"exampleFix":"// before\nconst token = `Bearer ${localStorage.getItem('token')}` // sent into x-token\nclaims, err := utils.ParseToken(token) // 这不是一个token\n// after\nconst token = localStorage.getItem('token').trim()\nclaims, err := utils.ParseToken(token) // ok","handlingStrategy":"validation","validationCode":"token = strings.TrimSpace(strings.TrimPrefix(rawToken, \"Bearer \"))\nif !looksLikeJWT(token) {\n    return errors.New(\"malformed token: expected header.payload.signature\")\n}","typeGuard":"func looksLikeJWT(s string) bool {\n    parts := strings.Split(s, \".\")\n    return len(parts) == 3 && parts[0] != \"\" && parts[1] != \"\" && parts[2] != \"\"\n}","tryCatchPattern":"claims, err := utils.ParseToken(token)\nif errors.Is(err, utils.TokenMalformed) {\n    // 400-level: the client sent garbage; do not retry with same token\n    return nil, ErrBadTokenFormat\n}","preventionTips":["Store the raw token without the 'Bearer ' prefix where the backend expects a bare JWT (x-token)","Trim whitespace/newlines/quotes from tokens before sending","Never truncate tokens when copying between tools; use clipboard-safe transfer"],"tags":["jwt","auth","go","malformed-token"],"backgroundTag":"jwt-token-malformed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}