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
相关产品推荐
相关产品推荐

