【问题标题】:Could not verify the provided CSRF token because your session was not found in spring security无法验证提供的 CSRF 令牌,因为在 Spring Security 中找不到您的会话
【发布时间】:2016-10-26 12:29:46
【问题描述】:

我正在使用 Spring Security 和 java 配置

@Override
protected void configure(HttpSecurity http) throws Exception { 
    http
    .authorizeRequests()
    .antMatchers("/api/*").hasRole("ADMIN")
    .and()
    .addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class)
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .formLogin()
    .successHandler(authenticationSuccessHandler)
    .failureHandler(new SimpleUrlAuthenticationFailureHandler());

我正在使用 PostMan 测试我的 REST 服务。我成功获得了“csrf token”,并且可以在请求标头中使用X-CSRF-TOKEN 登录。但是登录后,当我点击发布请求时(我在请求标头中包含了用于登录发布请求的相同令牌)我收到以下错误消息:

HTTP 状态 403 - 无法验证提供的 CSRF 令牌,因为找不到您的会话。

谁能指导我做错了什么。

【问题讨论】:

标签: java spring-security csrf spring-restcontroller


【解决方案1】:

这是一个老问题,但这可能会对某人有所帮助。我遇到了类似的问题,这就是我能够解决的方法。

为了让 CSRF 与 REST API 一起工作,您需要在每次调用之前通过 API 获取 CSRF 令牌并使用该令牌。 Token每次都不一样,不能重复使用。

这是获取 CSRF 令牌的控制器:

@RequestMapping(value = "/csrf", method = RequestMethod.GET)
    public ResponseEntity<CSRFDTO> getCsrfToken(HttpServletRequest request) {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        return ResponseEntity.ok(CSRFDTO.builder()
                .headerName(csrf.getHeaderName())
                .token(csrf.getToken())
                .build());
    }

此外,您可能会考虑配置您的 Spring 应用程序以禁用 REST API 端点的 CSRF。引用我在某处读过的一篇文章:

我非常确定 REST 端点上的 CSRF 令牌授予零额外保护。因此,在 REST 端点上启用 CSRF 保护只会向您的应用程序引入一些无用的代码,我认为应该跳过它。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我在不使用 credentials: "same-origin" 选项的情况下执行 JS fetch AJAX 调用时收到此错误消息 (HTTP Status 403 - Could not verify the provided CSRF token because your session was not found.)。

    错误的方式

    fetch(url)
    .then(function (response) { return response.json(); })
    .then(function (data) { console.log(data); })
    

    正确方法

    fetch(url, {
        credentials: "same-origin"
    })
    .then(function (response) { return response.json(); })
    .then(function (data) { console.log(data); })
    

    【讨论】:

      【解决方案3】:

      仅使用 POST 方法也出现相同的错误,收到 403 Forbidden “Could not verify the provided CSRF token because your session was not found.”

      在探索了一段时间后,通过将@EnableResourceServer 注解添加到配置中找到了解决方案。

      配置看起来像这样(spring-boot.version -> 1.4.1.RELEASE,spring-security.version -> 4.1.3.RELEASE,spring.version -> 4.3.4.RELEASE)

      @Configuration
      @EnableWebSecurity
      @EnableResourceServer
      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class SecurityConfig extends ResourceServerConfigurerAdapter {
      
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(inMemoryUserDetailsManager()).passwordEncoder(passwordEncoder());
      }
      
      @Override
      public void configure(HttpSecurity http) throws Exception {
          http.httpBasic();
          http.sessionManagement().sessionCreationPolicy(STATELESS);
          http.csrf().disable();
          http.authorizeRequests().anyRequest()
                  .permitAll();
      }
      
      private InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException {
          // load custom properties
          Properties properties = new Properties();
          return new InMemoryUserDetailsManager(properties);
      }
      
      private PasswordEncoder passwordEncoder() {
          return new TextEncryptorBasedPasswordEncoder(textEncryptor());
      }
      
      private TextEncryptor textEncryptor() {
          return new OpenSslCompatibleTextEncryptor();
      }
      
      }
      

      【讨论】:

        【解决方案4】:

        试试这个:@Override protected boolean sameOriginDisabled() { return true;}

        @Configuration
        public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
        
            ...
        
            // Determines if a CSRF token is required for connecting. This protects against remote
            // sites from connecting to the application and being able to read/write data over the
            // connection. The default is false (the token is required).
            @Override
            protected boolean sameOriginDisabled() {
                return true;
            }
        }
        

        来源:WebSocket Security: Disable CSRF within WebSockets

        【讨论】:

        • 这与 websockets 无关
        【解决方案5】:

        禁用 CSRF 保护是个坏主意。

        Spring 会在每次请求后自动生成一个新的 CSRF 令牌,您需要将其包含在所有带有副作用的 HTTP 请求中(PUT, POST, PATCH, DELETE).

        在 Postman 中,您可以在每个请求中使用测试将 CSRF 令牌存储在全局中,例如当使用CookieCsrfTokenRepository

        pm.globals.set("xsrf-token", postman.getResponseCookie("XSRF-TOKEN").value);
        

        然后将其包含为带有键 X-XSRF-TOKEN 和值 {{xsrf-token}} 的标头。

        【讨论】:

        • 你能为我们这些菜鸟详细解释一下你的答案吗:(
        【解决方案6】:

        根据spring.io:

        什么时候应该使用 CSRF 保护?我们的建议是使用 CSRF 保护可以由浏览器处理的任何请求 普通用户。如果您只创建一个由 非浏览器客户端,您可能希望禁用 CSRF 保护。

        所以要禁用它:

        @Configuration
        public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();
          }
        }
        

        注意: CSRF 保护在 Java 配置中默认启用

        【讨论】:

        • CSRF 保护在 Java 配置中默认启用。
        • 不推荐禁用 CSRF。如果我想使用 Postman 测试我的 API,但又希望启用 CSRF,因为该 API 将被浏览器使用,该怎么办?禁用 CSRF 是错误的做法
        • 建议对普通用户可以通过浏览器处理的任何请求使用 CSRF 保护。如果您只创建非浏览器客户端使用的服务,您可能需要禁用 CSRF 保护。
        【解决方案7】:

        我已经通过在我的登录页面中添加最后一个属性来解决它,也许它会帮您一个忙。

        <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"  isELIgnored="false"%>
        

        【讨论】:

        • 这不是我正在寻找的解决方案,因为正如我在问题中提到的那样,我实现了休息服务并且我从 restClient 发送请求而不是从 JSP 或 Web 浏览器。
        猜你喜欢
        • 2016-12-17
        • 2018-10-08
        • 1970-01-01
        • 2016-12-24
        • 1970-01-01
        • 2016-05-07
        • 2014-02-07
        • 2014-11-26
        相关资源
        最近更新 更多