Angular 18独立项目JWT授权头无法添加至请求的问题
问题描述
我正在开发首个Spring Boot+Angular 18独立项目,包含三个接口:
/register- 公开访问/login- 公开访问,返回token及过期时间/users- 仅持有效JWT的授权用户可访问,返回用户列表
后端生成JWT用于授权,前端需将token存入localStorage,并在登录后为所有请求添加Authorization: Bearer TOKEN头。后端经Postman测试功能正常,但前端虽能接收并存储token,请求却未携带该授权头。
已尝试添加AuthInterceptor并配置至main.ts,但问题仍未解决,相关代码如下:
LoginComponent
import { Component } from '@angular/core'; import { CardModule } from 'primeng/card'; import { InputTextModule } from 'primeng/inputtext'; import { FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder } from '@angular/forms'; import { ButtonModule } from 'primeng/button'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { ImageModule } from 'primeng/image'; import { AuthService } from '../../services/auth.service'; import { Router } from '@angular/router'; import { UserLoginDto } from '../../interfaces/userLoginDto'; import { UserService } from '../../services/user.service'; import { LoginResponse } from '../../interfaces/loginResponse'; @Component({ selector: 'app-login', standalone: true, imports: [ CardModule, InputTextModule, ReactiveFormsModule, ButtonModule, RouterModule, CommonModule, ImageModule ], templateUrl: './login.component.html', styleUrl: './login.component.css', }) export class LoginComponent { loginForm!: FormGroup; constructor( private fb: FormBuilder, private authService: AuthService, private router: Router, private userService: UserService ){} ngOnInit(): void { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], password: ['', Validators.required] }); } get email() { return this.loginForm.controls['email']; } get password() { return this.loginForm.controls['password']; } onSubmit(){ if(this.loginForm.valid){ const userLoginDto: UserLoginDto = { email: this.loginForm.value.email, password: this.loginForm.value.password }; this.userService.loginUser(userLoginDto).subscribe({ next: (response: LoginResponse) => { console.log('User logged in successfully', response); const token = response.token; this.authService.setAuthToken(token); this.router.navigate(['/home']); }, error: (err) => console.error('Error registering user', err) }); } } }
UserService
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { User } from '../interfaces/user'; import { UserRegisterDto } from '../interfaces/userRegisterDto'; import { UserLoginDto } from '../interfaces/userLoginDto'; import { LoginResponse } from '../interfaces/loginResponse'; @Injectable({ providedIn: 'root' }) export class UserService { private apiServerUrl = 'http://localhost:8080'; constructor(private http: HttpClient) { } public getAllUsers(): Observable<User[]>{ return this.http.get<User[]>(`${this.apiServerUrl}/users`); } public registerUser(user: UserRegisterDto): Observable<UserRegisterDto>{ return this.http.post<UserRegisterDto>(`${this.apiServerUrl}/register`, user); } public loginUser(user: UserLoginDto): Observable<LoginResponse>{ return this.http.post<LoginResponse>(`${this.apiServerUrl}/login`, user); } }
AuthService
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AuthService { private tokenKey: string = 'token'; getAuthToken(): string | null{ const token = window.localStorage.getItem(this.tokenKey); console.log("Retrieved token:", token); return token; } setAuthToken(token: string | null): void{ if(token !== null){ window.localStorage.setItem(this.tokenKey, token); } else{ window.localStorage.removeItem(this.tokenKey) } } }
AuthInterceptor
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { AuthService } from './services/auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor( private authService: AuthService, ){} intercept(req: HttpRequest<any>, next: HttpHandler){ const authToken = this.authService.getAuthToken(); const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${authToken}`) }); return next.handle(authReq); } }
main.ts
import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptors } from '@angular/common/http'; import { routes } from './app/app.routes'; import { provideRouter } from '@angular/router'; import { AuthInterceptor } from './app/auth-interceptor'; bootstrapApplication(AppComponent, { providers: [ provideRouter(routes), provideHttpClient(), { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) .catch((err) => console.error(err));
解决方案
针对Angular 18独立项目的拦截器配置问题,按以下步骤调整:
改用函数式拦截器(Angular 18适配方案)
Angular 18独立项目推荐使用函数式拦截器替代传统类拦截器,修改拦截器代码:// auth.interceptor.ts import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; import { AuthService } from './services/auth.service'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const authService = inject(AuthService); const authToken = authService.getAuthToken(); // 仅给需要授权的请求添加头,排除公开接口 if (authToken && !req.url.includes('/login') && !req.url.includes('/register')) { const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${authToken}`) }); return next(authReq); } return next(req); };更新main.ts的HTTP配置
移除HTTP_INTERCEPTORS提供者,改用withInterceptors注册函数式拦截器:// main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { routes } from './app/app.routes'; import { provideRouter } from '@angular/router'; import { authInterceptor } from './app/auth.interceptor'; bootstrapApplication(AppComponent, { providers: [ provideRouter(routes), provideHttpClient(withInterceptors([authInterceptor])) ] }) .catch((err) => console.error(err));修复拦截器逻辑漏洞
- 原代码未判断token是否存在,可能会生成
Bearer null的无效请求头 - 排除
/login、/register等公开接口,避免不必要的授权头添加
- 原代码未判断token是否存在,可能会生成
验证拦截器生效状态
在拦截器函数中添加日志,确认是否触发:export const authInterceptor: HttpInterceptorFn = (req, next) => { console.log('Interceptor triggered for URL:', req.url); // ... 其他代码 };登录后调用
/users接口,查看控制台日志及网络请求头是否包含Authorization。确认token存储正确性
登录后在控制台执行localStorage.getItem('token'),验证token是否正确存入。
内容的提问来源于stack exchange,提问作者jon1
相关产品推荐
相关产品推荐

