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

SpringBoot后端报Invalid JWT token错误,前端GET请求鉴权异常

JWT认证异常排查:sessionStorage存在token但Interceptor无法获取

访问SpringBoot后端http://localhost:8080/api/test/patients端点时,后端抛出错误:

Invalid JWT token: JWT strings must contain exactly 2 period characters. Found: 0

使用POST请求可正常访问该端点,推测问题出在前端。前端基于Angular开发,登录后sessionStorage中已存储auth-token,但AuthInterceptor中调用tokenStorage.getToken()返回undefined,导致请求未携带有效JWT,进而触发后端错误。

相关前端代码

AuthService

const api = 'http://locahost:8080/api/auth/'

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(private http: HttpClient) { }

  login(username: string, password: string): Observable<any> {
    return this.http.post(api + 'signin', {
      username,
      password
    }, httpOptions);
  }
}

UserService

const apiUrl = 'http://localhost:8080/api/test/'

@Injectable({
  providedIn: 'root'
})
export class UserService {

  constructor(private http : HttpClient) { }

 
  getAdminContent()  :Observable <any> {
    return this.http.get(apiUrl + 'patients', {responseType :'text'})
  }
}

TokenStorageService

const tokenKey  = 'auth-token';
const userKey = 'auth-user';
@Injectable({
  providedIn: 'root'
})
export class TokenStorageService {

  constructor(private router:Router) { }
  signOut() : void {
    
    window.sessionStorage.clear();

  }
  public saveToken(token:string) :void {
      window.sessionStorage.removeItem(tokenKey);
      window.sessionStorage.setItem(tokenKey, token)
  }
  public getToken() :string | null {
    return sessionStorage.getItem(tokenKey)
    
  }
  public saveUser( user : any) : void {
 window.sessionStorage.removeItem(userKey)
 window.sessionStorage.setItem(userKey , JSON.stringify(user))
  }
  public getUser(): any {
    const user = window.sessionStorage.getItem(userKey);
    if (user) {
      return JSON.parse(user);
    }

    return {};
  }
}

AuthInterceptor

const TOKEN_HEADER_KEY = 'Authorization';      


@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private token: TokenStorageService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let authReq = req;
    const token = this.token.getToken();
    if (token != null) {
 
      authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });

      
    }
    return next.handle(authReq);
  }
}

export const authInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

LoginComponent

export class LoginComponent implements OnInit {
form : any = {
username : null,
password: null

};
isLoggedIn = false;
isLoginFailed =  false;
errorMessage = '';
roles : string [] =  [ ];

  constructor(private authService: AuthService ,
     private tokenStorage: TokenStorageService , private router: Router, 
      private location: LocationStrategy) {
        history.pushState(null, "null", window.location.href); 
      this.location.onPopState(() => {
        history.pushState(null, "null", window.location.href);
      });
       }

  ngOnInit(): void {

    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getUser().roles;
      
    }
  }
  onSubmit(): void {
    const { username, password } = this.form;

    this.authService.login(username, password).subscribe(
      data => {
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUser(data);

        this.isLoginFailed = false;
        this.isLoggedIn = true;
        this.roles = this.tokenStorage.getUser().roles;
        this.router.navigate(['/home']).then(()=> window.location.reload());
        
      },
      err => {
        this.errorMessage = err.error.message;
        this.isLoginFailed = true;
      }
    );
  }
  reloadPage() {
 
    window.location.reload();
  }

}

排查情况

  • 打印const token = this.token.getToken();结果为undefined,但浏览器sessionStorage中已存在auth-token
  • 请求头中Authorization字段携带内容异常

相关截图

请求截图
sessionStorage截图1
sessionStorage截图2

排查建议

  1. 验证Token存储操作有效性:
    在saveToken方法执行后立即调用getToken并打印,确认token是否被成功写入sessionStorage,排除存储逻辑的隐性错误。
  2. 排查页面刷新的影响:
    LoginComponent中登录成功后执行了window.location.reload(),强制页面刷新会重启Angular应用。可在AppComponent的ngOnInit中调用tokenStorage.getToken()打印,确认应用启动时能否读取到token。同时可尝试替换强制刷新为Angular路由刷新:
    this.router.navigate(['/home'], { skipLocationChange: true }).then(() => {
      this.router.navigate(['/home']);
    });
    
    避免页面重载导致的状态读取延迟。
  3. 确认Interceptor注册状态:
    检查authInterceptorProviders是否在AppModule的providers数组中正确注册,未注册会导致Interceptor不生效,无法携带token。
  4. 检查Interceptor拦截范围:
    在AuthInterceptor的intercept方法中添加console.log(req.url),确认/api/test/patients请求是否进入拦截逻辑,排除请求未被拦截的情况。
  5. 校验sessionStorage作用域:
    确认前端页面的域名+端口与存储token的上下文一致,sessionStorage按域名+端口隔离,若存在跨域或端口差异会导致无法读取。
  6. 检查LoginComponent的token保存时机:
    确认saveToken执行完成后再进行页面跳转/刷新,避免token未完成写入就触发页面重载,导致读取时token还未持久化。

内容的提问来源于stack exchange,提问作者54V4G3208

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:55:18