使用 Spring Boot 3.0.0 和 Angular 15.2.4 的 PUT 上的 CSRF 问题

问题描述 投票:0回答:1

即使 CSRF 令牌和标头看起来设置正确,我也会收到 PUT 请求的 403

Spring Boot 日志:

2023-04-14T10:19:06.134+10:00 DEBUG 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:保护 PUT /api/incidents/1 2023-04-14T10:19:06.134+10:00 TRACE 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:调用 DisableEncodeUrlFilter (1/12) 2023-04-14T10:19:06.134+10:00 TRACE 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:调用 WebAsyncManagerIntegrationFilter (2/12) 2023-04-14T10:19:06.134+10:00 TRACE 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:调用 SecurityContextHolderFilter (3/12) 2023-04-14T10:19:06.134+10:00 TRACE 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:调用 HeaderWriterFilter (4/12) 2023-04-14T10:19:06.134+10:00 TRACE 19528 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy:调用 CsrfFilter (5/12) 2023-04-14T10:19:06.135+10:00 DEBUG 19528 --- [nio-8080-exec-2] o.s.security.web.csrf.CsrfFilter:为 http://localhost:8080/api 找到无效的 CSRF 令牌/事件/1
2023-04-14T10:19:06.135+10:00 DEBUG 19528 --- [nio-8080-exec-2] o.s.s.w.access.AccessDeniedHandlerImpl:响应 403 状态码

这是发出PUT请求的JS代码:

updateIncident(incident: Incident): Observable<any> {
      
    const url = `${this.incidentsUrl}/${incident.number}`;

    return this.http.put(url, incident).pipe(
      tap(_ => this.log(`updated incident number=${incident.number}`)),
      catchError(this.handleError<any>('updateIncident'))
    );

  }

这是 WebSecurityConfig 代码:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    
    /****************************************************************************/
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
            .authorizeHttpRequests((requests) -> requests
            .requestMatchers("/", "/home", "/contact").permitAll()
            .requestMatchers("/css/**", "/scripts/**", "/images/**").permitAll()
            .anyRequest().authenticated()
        ).formLogin((form) -> form.loginPage("/login").permitAll()
        ).logout((logout) -> logout.permitAll().logoutSuccessUrl("/login"));

        return http.build();
        
    }

Chrome 网络选项卡中的内容:

**Headers:**

**General:**
Request URL: http://localhost:8080/api/incidents/1
Request Method: PUT
Status Code: 403 
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin

**Response Headers:**
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Type: application/json
Date: Fri, 14 Apr 2023 00:28:56 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0

**Request Headers:**
t/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 400
Content-Type: application/json
Cookie: JSESSIONID=0D60F7FD506D0E4A6826CB64FEF7099D; XSRF-TOKEN=8f5a726a-e3a3-4c47-9ff2-bb41499c2e2a
Host: localhost:8080
Origin: http://localhost:8080
Referer: http://localhost:8080/incident-details/1
sec-ch-ua: "Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
X-XSRF-TOKEN: 8f5a726a-e3a3-4c47-9ff2-bb41499c2e2a

**Cookies:**

JSESSIONID  0D60F7FD506D0E4A6826CB64FEF7099D    localhost   /   Session 42  ✓               Medium  
XSRF-TOKEN  8f5a726a-e3a3-4c47-9ff2-bb41499c2e2a    localhost   /   Session 46                  Medium

我知道这已被问过一百万次,但除了 csrf().disable() 之外,我读过的所有答案都没有帮助。

我是新手,但我已经阅读了文档,据我所知,请求看起来不错。我错过了什么?谢谢:)

spring-boot csrf http-status-code-403
1个回答
0
投票

这对我有用:https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html#servlet-defer-loading-csrf-token-opt-out

这是我的代码:

/****************************************************************************/
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        
        CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
        CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
        // set the name of the attribute the CsrfToken will be populated on
        requestHandler.setCsrfRequestAttributeName("_csrf");
        http
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/", "/home", "/contact").permitAll()
                .requestMatchers("/css/**", "/scripts/**", "/images/**").permitAll()
                .anyRequest().authenticated())
            .formLogin((form) -> form.loginPage("/login").permitAll())
            .logout((logout) -> logout.permitAll().logoutSuccessUrl("/"))
            .csrf((csrf) -> csrf
                .csrfTokenRepository(tokenRepository)
                .csrfTokenRequestHandler(requestHandler)
            )
            .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);

        return http.build();
        
    }

    /****************************************************************************/
    
    private static final class CsrfCookieFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
            csrfToken.getToken();
            filterChain.doFilter(request, response);
        }

    }
© www.soinside.com 2019 - 2024. All rights reserved.