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

登录时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字段)
  • 服务器端检查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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:17:10