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

使用JWT保护Web应用时遇CORS错误及403响应问题求助

解决Angular + Spring Boot JWT认证中的CORS 403问题

我之前在做Angular+Spring Boot JWT认证的时候也踩过一模一样的坑——明明配置了CORS还是报403,后来发现是几个细节没处理到位,给你梳理一下排查方向:

1. 检查Spring Boot的CORS配置是否覆盖关键参数

很多时候CORS配置看起来没问题,但漏掉了允许携带凭证或者用了过时的配置项。比如Spring Boot 2.4+推荐用allowedOriginPatterns而非allowedOrigins,后者对带端口的域名支持有限。

给你一个完整的CORS配置示例,结合Spring Security一起用:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and() // 先启用CORS配置
            .csrf().disable() // JWT场景下通常关闭CSRF
            .authorizeRequests()
            // 必须放行OPTIONS预检请求,因为它不带JWT令牌
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            // 放行登录接口(获取JWT的入口)
            .antMatchers("/auth/login").permitAll()
            // 其他接口都需要认证
            .anyRequest().authenticated()
            // 添加JWT过滤器
            .and()
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    // 单独配置CORS的Bean
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许你的Angular应用域名,支持通配符或者具体域名
        config.setAllowedOriginPatterns(Arrays.asList("http://localhost:4200"));
        // 允许所有请求头,包括JWT所在的Authorization头
        config.setAllowedHeaders(Arrays.asList("*"));
        // 允许所有HTTP方法,明确包含OPTIONS
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        // 关键:允许携带凭证(JWT在请求头里,必须开这个)
        config.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有接口生效
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    // 这里替换成你的JWT过滤器实例
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

如果你的Spring Boot版本是3.x(用Spring Security 6+),配置方式略有不同,改用SecurityFilterChain:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .requestMatchers("/auth/login").permitAll()
                .anyRequest().authenticated())
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    // 同样的corsConfigurationSource Bean...
}

2. 确保JWT过滤器跳过OPTIONS预检请求

预检请求(OPTIONS)是浏览器自动发送的,不会携带JWT令牌。如果你的JWT过滤器直接拦截了所有请求并验证令牌,就会返回403。所以必须在过滤器里跳过OPTIONS请求:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 跳过OPTIONS方法的预检请求
        if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            filterChain.doFilter(request, response);
            return;
        }

        // 你的JWT令牌提取和验证逻辑
        String token = extractTokenFromRequest(request);
        if (token != null && jwtTokenUtil.validateToken(token)) {
            // 将认证信息存入SecurityContext
            Authentication auth = jwtTokenUtil.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        filterChain.doFilter(request, response);
    }

    // 提取请求头中的JWT令牌
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

3. Angular端请求必须携带凭证

在Angular的HttpClient请求中,需要显式设置withCredentials: true,否则浏览器不会在跨域请求中携带必要的头信息:

import { HttpClient, HttpHeaders } from '@angular/common/http';

// 假设你已经获取到了JWT令牌
const jwtToken = localStorage.getItem('jwtToken');

const headers = new HttpHeaders({
  'Authorization': `Bearer ${jwtToken}`
});

// 发送请求时带上withCredentials
this.http.get('http://localhost:8080/api/protected-data', { 
  headers, 
  withCredentials: true 
}).subscribe(
  response => console.log('请求成功:', response),
  error => console.error('请求失败:', error)
);

最后排查点

  • 确认浏览器控制台的CORS错误信息,看是Origin不被允许,还是Access-Control-Allow-Credentials没设置
  • 检查Spring Boot的日志,看OPTIONS请求是否被正确放行,还是被JWT过滤器拦截了
  • 如果你的JWT令牌存在Cookie中,还要确保Cookie的SameSite属性设置为Lax或者None(如果是跨域)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:50:07