【问题标题】:Spring Security Invalid CSRF token foundSpring Security 发现无效的 CSRF 令牌
【发布时间】:2015-12-09 02:51:33
【问题描述】:

我有一个带有 Spring MVC + Spring Security 后端的 Angular 前端,我的日志中出现以下错误:

DEBUG csrf.CsrfFilter Invalid CSRF token found for https://localhost:8443/rest/logout

要使用 Angular 设置 Spring Security,我按照下面的 Spring Boot 教程使其适合我的应用程序,该应用程序已经用 Spring MVC 和 Spring Security 而不是 Spring Boot 编写:https://spring.io/guides/tutorials/spring-security-and-angular-js/

以下是相关文件:

web.xml

<web-app version="3.1"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <!-- Log4j configuration loading -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.xml</param-value>
    </context-param>
    <!-- Bootstrapping context loading -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/gravytrack-servlet.xml
            /WEB-INF/gravytrack-services.xml
            /WEB-INF/gravytrack-security.xml
           </param-value>
    </context-param>
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>gravytrack.root</param-value>
    </context-param>

    <!-- session management listener -->
    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>
    <session-config>
        <!-- session times out if no activities for 30 minutes -->
        <session-timeout>30</session-timeout>
    </session-config>

    <servlet>
        <servlet-name>gravytrack</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>gravytrack</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <!--<servlet-mapping>-->
        <!--<servlet-name>gravytrack</servlet-name>-->
        <!--<url-pattern>*.*</url-pattern>-->
    <!--</servlet-mapping>-->

    <!-- Security entry point -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/rest/*</url-pattern>
    </filter-mapping>

    <error-page>
        <error-code>404</error-code>
        <location>/404.html</location>
    </error-page>
    <welcome-file-list>
        <welcome-file>
            index.html
    </welcome-file>
    </welcome-file-list>

</web-app>

gravytrack-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:util="http://www.springframework.org/schema/util"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util.xsd
                    http://www.springframework.org/schema/security
                    http://www.springframework.org/schema/security/spring-security.xsd">
    <global-method-security pre-post-annotations="enabled"
                            secured-annotations="enabled"/>

    <http auto-config="false" use-expressions="true">
        <intercept-url pattern="/rest/**" requires-channel="https"/>
        <access-denied-handler error-page="/"/>
        <http-basic entry-point-ref="gtBasicAuthenticationEntryPoint"/>

        <custom-filter ref="csrfHeaderFilter" after="CSRF_FILTER"/>
        <csrf token-repository-ref="csrfTokenRepository" />

        <logout logout-url="/rest/logout" logout-success-url="/rest/login?logout" invalidate-session="true" delete-cookies="JSESSIONID"/>

    </http>

    <beans:bean id="csrfTokenRepository" class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository">
        <beans:property name="headerName" value="X-XSRF-TOKEN" />
    </beans:bean>


    <beans:bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="passwordEncoder" />

    <authentication-manager alias="authenticationManager">
        <authentication-provider>
            <!--<password-encoder ref="passwordEncoder"/>-->
            <jdbc-user-service data-source-ref="dataSource"
                users-by-username-query="SELECT EMAIL as USERNAME, PASSWORD, ENABLED FROM USER_ACCOUNT WHERE EMAIL = ?"
                authorities-by-username-query="SELECT EMAIL as USERNAME, AUTHORITY FROM USER_AUTHORITY WHERE EMAIL = ?"/>

            <!--<user-service>-->
                <!--<user name="admin@admin.com" password="admin" authorities="ROLE_USER"/>-->
            <!--</user-service>-->
        </authentication-provider>
    </authentication-manager>

</beans:beans>

CsrfHeaderFilter.java

@Service("csrfHeaderFilter")
public class CsrfHeaderFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());

        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
            String token = csrf.getToken();
            if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/rest");

                response.addCookie(cookie);
            }
        }
        filterChain.doFilter(request, response);
    }
}

当我对 /rest/logout 进行 http 发布调用时,我在浏览器中收到 403 Forbidden 错误以及我在上面发布的日志中的 CSRF 错误。

如果我将我的 Angular 应用程序作为静态内容托管在调度程序 servlet 上,并且(在 web.xml 中)将 url 模式从 /rest/* 更改为 / 以及 Spring Security 入口点到 /* 那么它可以工作. CSRF 令牌通过过滤器很好,没有任何问题。

这让我相信,通过将调度程序 servlet url 模式和安全入口点更改为 /rest/*,它会以某种方式与 CSRF cookie 混淆,但我不知道如何。

任何帮助将不胜感激。

【问题讨论】:

    标签: spring spring-mvc spring-security


    【解决方案1】:

    我在 Spring 中也遇到过“找到无效的 CSRF 令牌”错误。 这对我来说很奇怪,因为主机和端口在我的请求中没有改变(只有路径)。 有一天我会尝试调试原因,同时暂时禁用 csrf 可以帮助您,请注意它可能会降低您的应用程序的安全性:

      protected void configure(HttpSecurity http) throws Exception {
            http.
               // ............
               .csrf().disable();
        }
    

    【讨论】:

    • 请记住,Stack Overflow 不仅仅是为了解决眼前的问题,而是为了帮助未来的读者找到类似问题的解决方案,这需要了解底层代码。这对于我们社区的初学者和不熟悉语法的成员来说尤其重要。鉴于此,您能否edit 您的答案包括对您正在做什么的解释以及为什么您认为这是最好的方法?
    • 它确实禁用了 CSRF 保护,这是一个坏主意,除非你真的确定你不需要 CSRF 保护。
    猜你喜欢
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 2012-03-06
    • 2019-07-20
    • 2014-07-03
    • 2015-08-25
    • 2014-12-06
    相关资源
    最近更新 更多