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)修复
- 在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)修复
开发环境推荐使用本地代理彻底规避跨域问题,配置步骤如下:
- 修正代理配置的目标端口,确保和后端服务端口一致:
{ "/api": { "target": "http://localhost:8081/", "changeOrigin": true, "secure": false, "pathRewrite": { "^/api": "" } } }
- 修改请求地址为代理前缀开头的相对路径,不要写死绝对地址,确保请求走代理:
// 原绝对地址http://localhost:8081/myapp/authenticate替换为代理路径 public baseUrl = '/api/myapp/authenticate';
- 删除请求头中所有无效的
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; }) ); }
- 启动Angular服务时指定加载代理配置:
ng serve --proxy-config proxy.config.json,也可以在angular.json中配置代理路径,避免每次启动手动加参数。
如果不想使用代理,也可以保持绝对地址请求,只需要删除无效的CORS请求头,同时确保后端CORS配置的allowedOrigins列表包含Angular开发服务的实际访问地址即可。
验证结果
配置修改完成后重启前后端服务,发起登录请求时可以看到浏览器先发送OPTIONS预检请求,收到合法跨域头后自动发送POST请求,请求正常进入控制器的令牌生成方法,返回正确的JWT令牌。
内容的提问来源于stack exchange,提问作者Rama
相关产品推荐
相关产品推荐

