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字段携带内容异常
相关截图



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

