Spring Security发现无效的CSRF令牌

时间:2015-12-09 02:51:34

标签: spring spring-mvc spring-security

我有一个带有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 post调用时,我在浏览器中收到403 Forbidden错误以及我在上面发布的日志中的CSRF错误。

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

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

非常感谢任何帮助。

0 个答案:

没有答案