Search code examples
javaspring-mvcspring-securitycsrfthymeleaf

How to automatically generate csrf tokens in spring security / thymeleaf without using @EnableWebMvcSecurity


I've been trying to figure out why the hidden csrf field is not automatically added to my login form. I'm using Spring 4.1.1 with Spring Security 4.0.1 and Thymeleaf 2.1.4.

All I can find to fix the issue is to either use the _csrf variable in thymeleaf to add the field manually (although _csrf is null for me), or use @EnableWebMvcSecurity in Java config. However, I am using xml to configure security and would like to keep it that way. What it all boils down to is: What can I add to the security xml to make the thymeleaf generate the csrf token field?

My current configuration is:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- **************************************************************** -->
    <!--  RESOURCE FOLDERS CONFIGURATION                                  -->
    <!--  Dispatcher configuration for serving static resources           -->
    <!-- **************************************************************** -->
    <mvc:resources mapping="/dist/**" location="file:/var/www/meubelplan/dist/"/>
    <mvc:resources mapping="/css/**" location="file:/var/www/meubelplan/css/"/>
    <mvc:resources mapping="/js/**" location="file:/var/www/meubelplan/js/"/>
    <mvc:resources mapping="/images/**" location="file:/var/www/meubelplan/images/"/>

    <!-- **************************************************************** -->
    <!--  SPRING ANNOTATION PROCESSING                                    -->
    <!-- **************************************************************** -->
    <mvc:annotation-driven/>
    <context:component-scan base-package="com.wwk.meubelplan"/>

    <!-- **************************************************************** -->
    <!--  SPRING SECURITY                                                 -->
    <!-- **************************************************************** -->

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="sem" password="rtyfghvbn" authorities="ROLE_USER" />
                <security:user name="winnie" password="ikbenwinnie" authorities="ROLE_USER" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

    <security:http pattern="/account/login" security="none"/>
    <security:http pattern="/account/logout" security="none"/>
    <security:http pattern="/account/create" security="none"/>
    <security:http use-expressions="false">
        <security:csrf/>
        <security:intercept-url pattern='/account/**' access='ROLE_USER' />
        <security:form-login login-page='/account/login' default-target-url='/account' always-use-default-target='true'/>
        <security:logout logout-url="/account/logout" delete-cookies="JSESSIONID" logout-success-url="/account/login"/>
    </security:http>

    <!-- **************************************************************** -->
    <!--  THYMELEAF-SPECIFIC ARTIFACTS                                    -->
    <!--  TemplateResolver <- TemplateEngine <- ViewResolver              -->
    <!-- **************************************************************** -->

    <bean id="templateResolver"
          class="org.thymeleaf.templateresolver.FileTemplateResolver">
        <property name="prefix" value="/var/www/meubelplan/" />
        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML5" />
        <property name="characterEncoding" value="UTF-8" />
        <property name="cacheable" value="false"/>
    </bean>

    <bean id="templateEngine"
          class="org.thymeleaf.spring4.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver" />
    </bean>

    <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine" />
        <property name="characterEncoding" value="UTF-8" />
    </bean>

</beans>

My thymeleaf template for the form is:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:include="partials/general/head"></head>
<body>

    <div class="container">

        <nav th:replace="partials/general/navbar"></nav>

        <div th:replace="partials/general/logobar"></div>

        <div class="row">
            <div class="col-md-6 col-md-offset-3">

                <br/><br/>

                <div class="panel panel-default">
                    <div class="panel-heading">Login om uw account gegevens te bekijken</div>
                    <div class="panel-body">
                        <form name="loginForm" method="POST" th:action="@{'~/account'}">
                            <div class="form-group">
                                <label for="username">Email address</label>
                                <input type="text" class="form-control" id="username" name="username" placeholder="Gebruikersnaam"/>
                            </div>
                            <div class="form-group">
                                <label for="password">Password</label>
                                <input type="password" class="form-control" id="password" name="password" placeholder="Password"/>
                            </div>
                            <button type="submit" class="btn btn-default" value="Submit">Inloggen</button>
                        </form>
                    </div>
                </div>

            </div>
        </div>

    </div><!-- /.container -->

    <span th:replace="partials/general/scripts"></span>

</body>
</html>

Thanks in advance for any pointers in the right direction :)

Regards,

Sem


Solution

  • I found the information I needed in this other question: spring security 3.2.0 csrf token not working in freemarker template

    The answer is this:

    Adding the following xml to your xml configuration will enable thymeleaf to automatically add the csrf token input to your forms.

    security.xml

    <bean id="requestDataValueProcessor" class="org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor"/>
    
    <bean id="csrfFilter" class="org.springframework.security.web.csrf.CsrfFilter">
        <constructor-arg>
            <bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository">
                <property name="headerName" value="X-SECURITY" />
            </bean>
        </constructor-arg>
    </bean>
    

    web.xml

    <filter>
        <filter-name>csrfFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
      </filter>
    
    <filter-mapping>
        <filter-name>csrfFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>