登录时JWT异常:Username未定义问题排查求助
Spring Boot + Angular登录JWT用户名获取异常排查方案
问题概述
Spring Boot后端+Angular前端应用,登录功能存在以下问题:
- 服务器端无法从JWT中捕获username,显示为
undefined,导致查询数据库时抛出“用户未找到”异常 - 客户端登录成功后,
strUserName变量始终为空,无法获取当前登录用户的用户名
服务器端代码问题点
核心异常代码
获取会话用户名的方法依赖HttpServletRequest的attribute,但未正确获取到值:
public String getSessionUsername() { if (oHttpServletRequest.getAttribute("username") instanceof String) { return oHttpServletRequest.getAttribute("username").toString(); } else { return null; } }
调用该方法的isAdmin()因获取不到用户名,触发用户未找到异常:
public Boolean isAdmin() { if (this.getSessionUsername() != null) { UserEntity oUserEntityInSession = oUserRepository.findByUsername(this.getSessionUsername()) .orElseThrow(() -> new ResourceNotFoundException("User not found")); return Boolean.FALSE.equals(oUserEntityInSession.getRole()); } else { return false; } }
客户端代码问题点
登录流程代码
登录请求发送成功,但后续无法从JWT中解析出用户名:
onSubmit() { if (this.loginForm.valid) { console.log("Username:", this.loginForm.value.username); this.oSessionService.login(this.loginForm.value.username, this.oCryptoService.getSHA256(this.loginForm.value.password)).subscribe({ next: (data: string) => { console.log(this.loginForm.value.username); this.oSessionService.setToken(data); console.log(data); this.oSessionService.emit({ type: 'login' }); this.oMatSnackBar.open("Login successful.", '', { duration: 2000 }); this.oRouter.navigate(['/home']); }, error: (error: HttpErrorResponse) => { this.status = error; this.oMatSnackBar.open("Error in login operation.", '', { duration: 2000 }); } }); } }
获取用户名的方法
从JWT解析用户名时返回空值:
getUsername(): string { if (this.isSessionActive()) { let token: string | null = localStorage.getItem('token'); if (!token) { return ""; } else { return this.parseJwt(token).name; } } else { return ""; } }
调用该方法后strUserName为空:
// `strUserName` remains empty this.strUserName = this.oSessionService.getUsername(); console.log(this.strUserName); this.oUserAjaxService.getByUsername(this.oSessionService.getUsername()).subscribe({ next: (oUser: IUser) => { this.oSessionUser = oUser; }, error: (error: HttpErrorResponse) => { console.log(error); } });
排查与修复步骤
1. 验证JWT本身的有效性
- 登录成功后,在客户端控制台打印返回的token,手动解析JWT的payload部分:
- 拆分token的第二部分(
.分隔的中间段),Base64解码后查看JSON内容 - 确认payload中是否包含用户名字段,字段名是
name、username还是sub(JWT标准的subject字段)
- 拆分token的第二部分(
- 服务器端检查JWT生成逻辑,确保用户名被正确写入claims中,例如:
// 示例:生成JWT时将用户名存入sub字段 Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact();
2. 修复服务器端用户名获取逻辑
当前依赖HttpServletRequest attribute的方式不可靠,建议改用Spring Security的SecurityContext获取:
public String getSessionUsername() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 排除匿名认证 if (authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken)) { return authentication.getName(); } return null; }
如果坚持使用request attribute,需检查JWT过滤器是否正确设置了request.setAttribute("username", 解析出的用户名),且过滤器拦截了所有需要认证的请求。
3. 修复客户端JWT解析逻辑
- 检查
parseJwt函数是否正确实现,确保能正确解码JWT payload:parseJwt(token: string): any { try { const base64Url = token.split('.')[1]; const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); const jsonPayload = decodeURIComponent( atob(base64) .split('') .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) .join('') ); return JSON.parse(jsonPayload); } catch (error) { console.error("JWT解析失败:", error); return {}; } } - 根据JWT payload中的实际字段名修改
getUsername的取值,例如如果字段是sub:return this.parseJwt(token).sub; - 验证
isSessionActive()的逻辑,确保它正确判断token是否存在且有效(例如检查过期时间)。
4. 端到端流程验证
- 客户端登录后,检查localStorage中是否正确存储了token
- 调用
getUsername前,先打印parseJwt(token)的结果,确认用户名字段存在且值正确 - 服务器端调试
getSessionUsername方法,确认返回的用户名与数据库中存在的用户一致
内容的提问来源于stack exchange,提问作者Elena Ortega
相关产品推荐
相关产品推荐

