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

Angular+Spring配置CSRF后仍返回403错误求助

Angular + Spring CSRF防护配置后POST请求403 Forbidden问题解决

最近配置Angular + Spring的CSRF防护时遇到问题:按常规配置在Spring安全过滤器链中添加csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())后,Angular请求已携带XSRF-TOKEN Cookie和X-XSRF-TOKEN请求头,但POST请求依然返回403 Forbidden。禁用CSRF防护后请求正常,但项目上线需要保留防护机制。


现有配置

Spring Security配置

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf((csrf) -> csrf
                .csrfTokenRepository(
                        CookieCsrfTokenRepository
                            .withHttpOnlyFalse()))
//      .csrf(AbstractHttpConfigurer::disable)
        .cors(withDefaults())
        .authorizeHttpRequests((authorize) -> authorize
                .anyRequest()
                .permitAll());

    return http.build();
}

Spring Controller POST接口

@PostMapping("/api/persons/new")
public ResponseEntity<?> savePerson(@RequestBody PersonDTO personDTO) {
    personService.addPerson(personDTO);
    return ResponseEntity.ok("\"Person added\"");
}

Angular服务调用代码

constructor(private http: HttpClient) {
    this.personApi = '//localhost:8080/api/persons';
}

public submitNewPerson(person: Person): Observable<Person>{    
    return this.http.post<Person>(
        `${this.personApi}/new`, person, {withCredentials: true}
    )
}

请求与响应信息

请求头(POST请求)

POST /api/persons/new HTTP/1.1
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Content-Length: 21
Content-Type: application/json
Cookie: XSRF-TOKEN=f863317c-9405-4534-8edd-1cd87edfc557
Host: localhost:8080
Origin: http://localhost:4200
Referer: http://localhost:4200/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
X-XSRF-TOKEN: f863317c-9405-4534-8edd-1cd87edfc557
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"

响应信息

{
    "timestamp": "2024-01-12T11:28:41.405+00:00",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/api/persons/new"
}

响应头

HTTP/1.1 403 
Vary: Origin 
Vary: Access-Control-Request-Method 
Vary: Access-Control-Request-Headers 
Access-Control-Allow-Origin: http://localhost:4200 
Access-Control-Allow-Credentials: true
X-Content-Type-Options: nosniff 
X-XSS-Protection: 0 
Cache-Control: no-cache, no-store, max-age=0, must-revalidate 
Pragma: no-cache Expires: 0 
X-Frame-Options: DENY 
Content-Type: application/json 
Transfer-Encoding: chunked 
Date: Fri, 12 Jan 2024 11:28:41 GMT 
Keep-Alive: timeout=60 Connection: keep-alive

可能的解决方法

1. 统一CSRF请求头名称

Spring默认期望的CSRF请求头是X-CSRF-TOKEN,而Angular自动发送的是X-XSRF-TOKEN,需要在Spring配置中指定匹配的头名称:

CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setHeaderName("X-XSRF-TOKEN");
http.csrf(csrf -> csrf.csrfTokenRepository(tokenRepository));

2. 先发送GET请求获取CSRF Token

如果首次请求直接是POST,Spring可能还未生成XSRF-TOKEN Cookie。可以在Angular初始化时先调用一个无副作用的GET接口提前获取Token:

constructor(private http: HttpClient) {
    this.personApi = '//localhost:8080/api/persons';
    // 初始化时获取CSRF Token
    this.http.get(`${this.personApi}/health`, {withCredentials: true}).subscribe();
}

3. 完善CORS配置

默认CORS配置可能未明确允许X-XSRF-TOKEN头,可自定义CORS配置确保覆盖所有必要项:

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

然后在Security配置中替换默认CORS:

http.cors(cors -> cors.configurationSource(corsConfigurationSource()))

4. 开启调试日志定位问题

添加Spring Security调试日志,查看CSRF验证的详细过程:

logging.level.org.springframework.security=DEBUG

重点查看CsrfFilter相关日志,确认Token是否被正确解析、匹配。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 11:34:57