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

Angular HttpClient在catchError中无法获取正确状态码问题

问题分析与解决方案

你的核心问题是:Token过期后端返回401,但前端拦截器拿到的error.status始终为0,导致无法触发刷新Token逻辑。这确实和CORS配置冲突有关,具体原因及解决方法如下:

1. 核心原因:Spring Security的CORS配置冲突

你在SecurityConfig中同时做了两件矛盾的事:

  • 调用http.cors().disable()禁用了Spring Security的CORS支持
  • 又定义了corsConfigurationSource Bean来配置跨域规则

这种情况下,自定义的CORS配置不会生效。当Token过期后端返回401时,响应没有携带正确的CORS头,浏览器出于安全策略拦截了该响应,导致前端只能拿到status=0的错误(这是浏览器跨域拦截的典型表现)。

2. 解决方案步骤

步骤1:修复Spring Security的CORS配置

移除http.cors().disable(),让Spring Security自动使用你定义的corsConfigurationSource Bean:

修改后的SecurityConfig配置代码:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors() // 保留cors(),启用自定义CORS配置
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();

        // Add our custom JWT security filter
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(new AlwaysSendUnauthorized401AuthenticationEntryPoint());
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource()
    {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200", "http://localhost:8100"));
        configuration.setAllowedMethods(Arrays.asList("HEAD", "GET","POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        // 允许携带认证信息(比如Cookie、Token)
        configuration.setAllowCredentials(true);
        // 允许前端获取响应头
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

步骤2:确保401错误响应携带CORS头

检查你的AlwaysSendUnauthorized401AuthenticationEntryPoint实现,确保它在返回401时也设置了CORS头,避免浏览器拦截错误响应:

public class AlwaysSendUnauthorized401AuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // 从请求头获取Origin,动态设置允许的跨域源(更灵活)
        String origin = request.getHeader("Origin");
        if (origin != null) {
            response.setHeader("Access-Control-Allow-Origin", origin);
        }
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token已过期或无效");
    }
}

步骤3:前端拦截器的小优化

可以在拦截器中增加对error.status === 0的判断,结合错误特征处理跨域导致的401拦截场景(可选,但更健壮):

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<Object>> {
    let authReq = req;
    const token = this.tokenService.getToken();
    if (token != null) {
      authReq = this.addTokenHeader(req, token);
    }
    return next.handle(authReq).pipe(catchError(error => {
      console.log(error);
      // 增加对status=0的判断,结合是否是认证相关请求
      const isAuthError = (error instanceof HttpErrorResponse && 
                          !authReq.url.includes('auth/login') && 
                          (error.status === 401 || (error.status === 0 && error.message.includes('CORS'))));
      if (isAuthError) {
        return this.handle401Error(authReq, next);
      } else{
        console.log(error, authReq, error.status);
      }
      return throwError(error);
    }));
}

3. 验证逻辑

修改完成后,当Token过期时:

  1. 后端会正确返回带有CORS头的401响应
  2. 浏览器不再拦截该响应,前端拦截器能拿到真实的error.status=401
  3. 自动触发handle401Error逻辑刷新Token,继续原请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:41:11