{"record":{"id":"ab64b6d5dbe73cbe","repo":"jeecgboot/JeecgBoot","slug":"error","errorCode":null,"errorMessage":"未获取到用户","messagePattern":"未获取到用户","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java","lineNumber":201,"sourceCode":"\t\t\treturn oConvertUtils.isNotEmpty(clientType) ? clientType : CommonConstant.CLIENT_TYPE_PC;\n\t\t} catch (JWTDecodeException e) {\n\t\t\tlog.warn(\"解析token中的clientType失败，使用默认值PC：\" + e.getMessage());\n\t\t\treturn CommonConstant.CLIENT_TYPE_PC;\n\t\t}\n\t}\n\n\t/**\n\t * 根据request中的token获取用户账号\n\t * \n\t * @param request\n\t * @return\n\t * @throws JeecgBootException\n\t */\n\tpublic static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {\n\t\tString accessToken = request.getHeader(\"X-Access-Token\");\n\t\tString username = getUsername(accessToken);\n\t\tif (oConvertUtils.isEmpty(username)) {\n\t\t\tthrow new JeecgBootException(\"未获取到用户\");\n\t\t}\n\t\treturn username;\n\t}\n\t\n\t/**\n\t  *  从session中获取变量\n\t * @param key\n\t * @return\n\t */\n\tpublic static String getSessionData(String key) {\n\t\t//${myVar}%\n\t\t//得到${} 后面的值\n\t\tString moshi = \"\";\n\t\tString wellNumber = WELL_NUMBER;\n\n\t\tif(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){\n\t\t\t moshi = key.substring(key.indexOf(\"}\")+1);\n\t\t}","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java#L183-L219","documentation":"JwtUtil.getUserNameByToken extracts the X-Access-Token header, decodes the JWT, and reads the username claim. If the decoded token yields an empty/null username (token malformed, missing username claim, or decode failure upstream), it throws JeecgBootException('未获取到用户'). This is an authentication/integrity guard: a request that presents a token but yields no principal must not proceed.","triggerScenarios":"A request carries an X-Access-Token that is expired, tampered, signed with a different key, or correctly signed but missing the username claim. Also when the token format changed (custom claim name) and JwtUtil.getUsername still reads the old claim path.","commonSituations":"Token expiry window edge cases; token generated by a different deployment with a different secret; a token minted by an older code version using a different username claim key; frontend sending a stale token from localStorage after a server-side key rotation.","solutions":["Have the client re-authenticate to obtain a fresh token.","Verify the JWT signing secret (application.yml: jeecg.jwt.secret) matches across all nodes/services that mint and validate tokens.","Confirm the username claim name in the token matches what JwtUtil.getUsername expects.","Decode the token at jwt.io to confirm the payload contains the username claim."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Decode and check the username claim before trusting the token\nimport io.jsonwebtoken.Jwts;\nClaims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();\nif (claims.get(\"username\") == null) {\n  throw new AuthException(\"Token missing username claim\");\n}","typeGuard":"public static boolean tokenHasUsername(String token) {\n  try { return JwtUtil.getUsername(token) != null; }\n  catch (Exception e) { return false; }\n}","tryCatchPattern":"try {\n  String username = JwtUtil.getUserNameByToken(request);\n} catch (JeecgBootException e) {\n  // token invalid/expired -> return 401\n  response.sendError(401, \"Invalid token\");\n}","preventionTips":["Keep JWT signing secrets consistent across services.","Re-authenticate when tokens expire.","Verify the username claim name matches the encoder configuration."],"tags":["backend","jwt","auth","security","jeecg"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}