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

Angular POST以OPTIONS提交SpringBoot未触发JWT生成接口排查

问题根因

核心问题是跨域预检(OPTIONS)请求处理逻辑存在多处配置错误,具体如下:

  • 浏览器环境下,Angular前端运行在本地开发服务器(默认端口4200),请求8081端口的后端接口属于跨域请求,浏览器会自动先发送OPTIONS方法的预检请求,校验后端返回的CORS响应头合法后,才会发送真正的POST业务请求。Postman不属于浏览器环境,不会自动触发CORS预检流程,因此可以直接调用POST接口成功。
  • 当前Spring Security配置仅放行OPTIONS请求的访问权限,但没有返回合法的CORS响应头,浏览器收到OPTIONS的200响应后校验跨域头不通过,直接拦截了后续的真实POST请求,导致POST请求根本没有发送到后端。
  • Angular请求代码中添加的Access-Control-Allow-*系列头属于无效配置:这类头是后端返回给浏览器的响应头,不是前端发起请求时需要携带的请求头,前端手动添加这类头反而会触发浏览器更严格的预检校验。
  • Angular代理配置完全未生效:代理规则仅对相对路径的请求生效,当前代码写死了http://localhost:8081开头的绝对请求地址,请求直接绕过开发服务器代理发送到后端,跨域问题无法通过代理规避。
  • 代码存在额外逻辑bug:Angular端登录成功后写死了空字符串拼接Bearer前缀存入sessionStorage,后续携带该无效token请求受保护接口会直接认证失败。
修复方案

后端(Spring Boot)修复

  1. 在Spring Security配置中开启CORS支持,添加全局CORS配置Bean,自动给所有响应添加合法的跨域头,不需要手动写CORS过滤器:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 新增全局CORS配置
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 配置允许跨域的前端地址,生产环境替换为正式前端域名
        configuration.setAllowedOrigins(List.of("http://localhost:4200"));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors() // 开启CORS,自动注入上面的跨域配置
                .and().csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 优先放行所有OPTIONS预检请求
                .antMatchers("/authenticate", "/myapp/authenticate").permitAll() // 放行登录接口
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

说明:JwtFilter中打印的JWT Token does not begin with Bearer String日志属于正常现象,OPTIONS预检请求本身不会携带Authorization认证头,不需要针对OPTIONS请求修改JwtFilter逻辑。

前端(Angular)修复

开发环境推荐使用本地代理彻底规避跨域问题,配置步骤如下:

  1. 修正代理配置的目标端口,确保和后端服务端口一致:
{
  "/api": {
    "target": "http://localhost:8081/",
    "changeOrigin": true,
    "secure": false,
    "pathRewrite": {
      "^/api": ""
    }
  }
}
  1. 修改请求地址为代理前缀开头的相对路径,不要写死绝对地址,确保请求走代理:
// 原绝对地址http://localhost:8081/myapp/authenticate替换为代理路径
public baseUrl = '/api/myapp/authenticate';
  1. 删除请求头中所有无效的Access-Control-Allow-*配置,修正token存储逻辑,不要写死空token:
public callService(userInfo:User){
  const username = userInfo.username;
  const password = userInfo.password;
  return this.http.post(this.baseUrl,
    { username, password },
    { 
      headers: new HttpHeaders({
        'Content-Type': 'application/json'
      }), 
      observe: 'response'
    }
  ).pipe(
    map(userData => {
      sessionStorage.setItem("username", username);
      // 从接口返回体中获取真实生成的JWT令牌
      const resBody = userData.body as any;
      sessionStorage.setItem("token", `Bearer ${resBody.token}`);
      return userData;
    })
  );
}
  1. 启动Angular服务时指定加载代理配置:ng serve --proxy-config proxy.config.json,也可以在angular.json中配置代理路径,避免每次启动手动加参数。

如果不想使用代理,也可以保持绝对地址请求,只需要删除无效的CORS请求头,同时确保后端CORS配置的allowedOrigins列表包含Angular开发服务的实际访问地址即可。

验证结果

配置修改完成后重启前后端服务,发起登录请求时可以看到浏览器先发送OPTIONS预检请求,收到合法跨域头后自动发送POST请求,请求正常进入控制器的令牌生成方法,返回正确的JWT令牌。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:42:18