MissingCsrfTokenException:无法验证提供的 CSRF 令牌,因为找不到您的会话

Posted

技术标签:

【中文标题】MissingCsrfTokenException:无法验证提供的 CSRF 令牌,因为找不到您的会话【英文标题】:MissingCsrfTokenException: Could not verify the provided CSRF token because your session was not found 【发布时间】:2018-10-08 10:42:42 【问题描述】:

我正在阅读 spring 文档:Adding CSRF to Stomp Header

我尝试将 stom 标头添加到连接事件,但我在客户端收到错误:

>>> CONNECT
XSRF-TOKEN:f86232c1-e877-46e9-b4e6-7427c3d89940
accept-version:1.1,1.0
heart-beat:10000,10000

<<< ERROR
message:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.web.csrf.MissingCsrfTokenException\c Could not verify the provided CSRF token because your session was not found.
content-length:0

客户端代码:

<script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script>

var headers = ;
    var headerName = "XSRF-TOKEN";
    var token = Cookies.get('XSRF-TOKEN')
    headers[headerName] = token;
    stompClient.connect(headers, function (frame) ....);

websocket 安全配置:

@Configuration
    public class WebSocketAuthorizationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer 
        @Override
        protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) 
            // You can customize your authorization mapping here.
            messages.anyMessage().authenticated();
            messages.simpDestMatchers("/app/hello").authenticated()//.hasRole("ADMIN")
                    .simpSubscribeDestMatchers("/user/queue/**").hasRole("ADMIN")
                    .simpSubscribeDestMatchers("/topic/greetings").authenticated();
        

        // TODO: For test purpose (and simplicity) i disabled CSRF, but you should re-enable this and provide a CRSF endpoint.
        @Override
        protected boolean sameOriginDisabled() 
            return false; //! I do it especially
        
    

弹簧安全配置:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    private static final String SECURE_ADMIN_PASSWORD = "rockandroll";

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .csrf().disable()
                .formLogin()
                .loginPage("/index.html")
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/sender.html")
                    .permitAll()
                .and()
                .logout()
                    .logoutSuccessUrl("/index.html")
                    .permitAll()
                .and()
                .authorizeRequests()
                .antMatchers("/js/**", "/lib/**", "/images/**", "/css/**", "/index.html", "/","/*.css","/webjars/**", "/*.js").permitAll()
                .antMatchers("/websocket").hasRole("ADMIN")
                .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN")
                .anyRequest().authenticated();

    

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 

        auth.authenticationProvider(new AuthenticationProvider() 

            @Override
            public boolean supports(Class<?> authentication) 
                return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
            

            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException 
                UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;

                List<GrantedAuthority> authorities = SECURE_ADMIN_PASSWORD.equals(token.getCredentials()) ?
                        AuthorityUtils.createAuthorityList("ROLE_ADMIN") : null;

                return new UsernamePasswordAuthenticationToken(token.getName(), token.getCredentials(), authorities);
            
        );
    

也许我的 csrf 令牌标头名称错误?

附言

【问题讨论】:

您是否将 CSRF Coo​​kie 与您的请求一起发送?如果我不发送 cookie,我会收到该错误。它必须具有正确的域才能让您的浏览器发送它。 @Will M,我该如何检查? 这取决于您使用什么来发送请求。如果它来自浏览器,通过网站,那么您应该使用该浏览器的开发人员工具。如果你是通过邮递员或其他工具制作的,它应该有一种内置的方式来查看你的 cookie 是什么 我无法编辑这篇文章,因为它主要是代码,不会让我编辑,如果其他人可以,请用这个完整的链接替换 ​​Adding CSRF to Stomp Header。 【参考方案1】:

这个问题不在 Spring Framework 中。更新到 Spring-Boot(2.5.0,war+Tomcat 9 实现,OAuth2)后,我遇到了同样的问题。

它对我有用:

    第 1 步(为 Websockets 添加安全配置 - 您需要禁用 SCRF Websocket Protection)

    导入 org.springframework.context.annotation.Configuration; 导入 org.springframework.messaging.simp.SimpMessageType; 导入 org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry; 导入 org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;

    @配置 公共类 SocketSecurityConfig 扩展 AbstractSecurityWebSocketMessageBrokerConfigurer

     @Override
     protected boolean sameOriginDisabled() 
         return true;
     
    
    
     @Override
     protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) 
         messages
                 .simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.HEARTBEAT, SimpMessageType.UNSUBSCRIBE, SimpMessageType.DISCONNECT).permitAll()
                 .anyMessage().permitAll()
                 .simpDestMatchers("/**").permitAll()
         ;
     
    

    第 2 步(在 Web 安全配置中添加对 Websockets 和 Stomp 的访问):

    导入 lombok.RequiredArgsConstructor; 导入 lombok.extern.slf4j.Slf4j; 导入 org.springframework.beans.factory.annotation.Autowired; 导入 org.springframework.context.annotation.ComponentScan; 导入 org.springframework.context.annotation.Configuration; 导入 org.springframework.security.config.annotation.web.builders.HttpSecurity; 导入 org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 导入 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 导入 org.springframework.security.config.http.SessionCreationPolicy;

    @Slf4j @EnableWebSecurity @ComponentScan("com.example.your.project.package.name") @配置 @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 公共类 OAuth2LoginSecurityConfig 扩展 WebSecurityConfigurerAdapter

     private static final String[] OPEN_URIS = 
             "/**/*.woff",
             "/**/*.woff2",
             "/**/*.ttf",
             "/webjars/**",
             "/login",
             "/login/*",
             "/login/*/**",
             "/resources/**",
             "/error",
             "/error/*",
             "/error/*/**",
             "/oauth2/authorization/**",
             "/**/*.png",
             "/**/*.css",
             "/**/*.js",
             "/style.css",
             "/favicon.ico",
             "/wicket",
             "/wicket/resource/",
             "/wicket/resource/*",
             "/wicket/resource/*/**",
             "/wicket/resources/**",
             "/wicket/resources/*/**",
             "/wicket/*",
             "/wicket/page/*",
             "/wicket/page/*/**",
             "/webjars",
             "/webjars/*",
             "/webjars/*/**",
             "/webservices/websocket/",
             "/webservices/websocket/*",
             "/webservices/websocket/*/**",
             "/webservices/websocketHandlerEndpoint",
             "/webservices/websocketHandlerEndpoint/*",
             "/webservices/websocketHandlerEndpoint/*/**"
     ;
    
     /////---Here some Configuration (not showed)------------ /////
    
    
     @Override
     protected void configure(HttpSecurity http) throws Exception 
    
         http.csrf().disable()
                 .headers()
                 .frameOptions().sameOrigin()
                 .httpStrictTransportSecurity().disable()
                 .and()
                 .sessionManagement()
                 .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                 .and()
                 .authorizeRequests(authorize -> authorize.antMatchers(OPEN_URIS).permitAll())
                 // (Optional  : )
                 .oauth2Login(oauth2 ->
                                 oauth2.permitAll() // <-- actually not real, but here must be code
                         //....`enter code here`..
                         //
                 )
                 //.....
                 .logout()
         // ...
         ;
     
    
     //another part of Configuration .....
    

注意: 1)"/webservices/websocket/..." - 是 Stomp 端点 2) "/webservices/websocketHandlerEndpoint..." - 是 Websocket 处理程序端点

【讨论】:

以上是关于MissingCsrfTokenException:无法验证提供的 CSRF 令牌,因为找不到您的会话的主要内容,如果未能解决你的问题,请参考以下文章