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

Postman请求Spring Boot授权头正常,Angular请求授权头为空且遭CORS拦截

问题排查:Angular请求Spring Boot接口时Authorization头为空且触发CORS错误

错误信息

从源地址'http://localhost:4200'访问'http://localhost:8080/api/v1/users'的XMLHttpRequest被CORS策略阻止:预检请求的响应未通过访问控制检查:请求资源上不存在'Access-Control-Allow-Origin'头。

Angular相关代码

服务代码

getUsersList(): Observable<User[]>{ 
    return this.httpClient.get<User[]>(`${this.baseUrl}`);
}

拦截器代码

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest,  } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthenticationService } from './auth-service';

@Injectable({
  providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {

  constructor(private authService: AuthenticationService) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
    const jwtToken = this.authService.getJwt();
    
    if(jwtToken){
      const header = req.clone({
        headers: req.headers.set('Authorization', 'Bearer ' + jwtToken)
      });
      console.log(header);
      return next.handle(header);
    }

    return next.handle(req);

  }
}

Spring Boot相关配置

跨域配置

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins("http://localhost:4200")
            .allowedMethods("*")
            .allowedHeaders("**")
            .exposedHeaders("Authorization")
            .allowCredentials(true)
            .maxAge(3600);
}

安全过滤器配置

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http.csrf(csrf -> csrf.disable())
            .authorizeRequests()
            //.requestMatchers("/test").authenticated()
            .requestMatchers("/api/v1/login/**").permitAll()
            .requestMatchers("/api/v1/authenticate/get-jwt").permitAll()
            .requestMatchers("/api/v1/users/**").authenticated()
            .requestMatchers("/api/v1/user/address/**").authenticated()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling(ex -> ex.authenticationEntryPoint(point))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

当前问题

使用Postman请求Spring Boot接口时Authorization头非空,但使用Angular请求时该头为空,同时触发上述CORS拦截错误。


解决方案

1. 确保Angular拦截器已正确注册

拦截器需要在应用模块的providers数组中声明才会生效,检查你的app.module.ts是否添加了以下配置:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { TokenInterceptorService } from './path/to/token-interceptor.service';

@NgModule({
  // ... 其他配置
  providers: [
    { 
      provide: HTTP_INTERCEPTORS, 
      useClass: TokenInterceptorService, 
      multi: true 
    }
  ]
})
export class AppModule { }

如果缺少这一步,拦截器不会被触发,请求自然不会带上Authorization头。

2. 修复Spring Security的CORS配置

单独通过addCorsMappings配置跨域在Spring Security场景下可能不生效,因为Security过滤器链会优先处理请求,预检的OPTIONS请求可能被拦截。修改安全过滤器配置,添加CORS支持并允许OPTIONS请求:

import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.CorsConfigurationSource;
import java.util.Arrays;
import org.springframework.http.HttpMethod;

// ... 其他代码

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http.csrf(csrf -> csrf.disable())
            .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用Security级别的CORS
            .authorizeRequests()
            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许所有OPTIONS预检请求
            .requestMatchers("/api/v1/login/**").permitAll()
            .requestMatchers("/api/v1/authenticate/get-jwt").permitAll()
            .requestMatchers("/api/v1/users/**").authenticated()
            .requestMatchers("/api/v1/user/address/**").authenticated()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling(ex -> ex.authenticationEntryPoint(point))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
    configuration.setExposedHeaders(Arrays.asList("Authorization"));
    configuration.setAllowCredentials(true);
    configuration.setMaxAge(3600L);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

3. 验证Token获取逻辑

在Angular拦截器中添加日志,确认authService.getJwt()确实能返回有效Token:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
    const jwtToken = this.authService.getJwt();
    console.log("获取到的Token:", jwtToken); // 添加这行日志
    // ... 后续代码
}

如果日志显示Token为空,需要检查authService中存储和获取Token的逻辑是否正确。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 17:05:56