title: Spring Boot + Security + Thymeleaf and CSRF token not injected automatically
body: <p><strong>Disclaimer</strong>: I know how to inject the token in a form with thymeleaf manually with this:</p>
  
  <pre><code>&lt;input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /&gt;`
  </code></pre>
  
  <p>The goal of this post is to improve the knowledge of the platform and get a better understanding of what's going on inside Spring Boot</p>
  
  <p>I haven't tried Spring Boot, but recently i just decided to give it a try, and have to admit its awesome, but with Thymeleaf and Security on Spring MVC, i didn't need to inject CSRF token on forms (POST), because Thymeleaf took care of it automatically, but now in Spring Boot for some reason it doesn't.</p>
  
  <p>From the <a href="http://docs.spring.io/spring-boot/docs/1.2.3.RELEASE/reference/htmlsingle/#common-application-properties" rel="noreferrer">Spring Boot Reference</a>, i found a list of the common properties used on application.properties file, and the ones related to thymeleaf and security are: </p>
  
  <p>Thymeleaf Properties</p>
  
  <pre><code>spring.thymeleaf.check-template-location=true
  spring.thymeleaf.prefix=classpath:/templates/
  spring.thymeleaf.excluded-view-names= # comma-separated list of view names that should be excluded from resolution
  spring.thymeleaf.view-names= # comma-separated list of view names that can be resolved
  spring.thymeleaf.suffix=.html
  spring.thymeleaf.mode=HTML5
  spring.thymeleaf.encoding=UTF-8
  spring.thymeleaf.content-type=text/html # ;charset=&lt;encoding&gt; is added
  spring.thymeleaf.cache=true # set to false for hot refresh
  </code></pre>
  
  <p>Security Properties</p>
  
  <pre><code>security.user.name=user # login username
  security.user.password= # login password
  security.user.role=USER # role assigned to the user
  security.require-ssl=false # advanced settings ...
  security.enable-csrf=false
  security.basic.enabled=true
  security.basic.realm=Spring
  security.basic.path= # /**
  security.basic.authorize-mode= # ROLE, AUTHENTICATED, NONE
  security.filter-order=0
  security.headers.xss=false
  security.headers.cache=false
  security.headers.frame=false
  security.headers.content-type=false
  security.headers.hsts=all # none / domain / all
  security.sessions=stateless # always / never / if_required / stateless
  security.ignored= # Comma-separated list of paths to exclude from the     default secured paths
  </code></pre>
  
  <p>But if the solution to make Thymeleaf inject the token again is there, i fail to see it. </p>
  
  <p><strong>Edit</strong>: adding my configuration</p>
  
  <p>The project was created using the initializer that was shipped in the last STS version (which in my opinion is awesome), with Web, Thymeleaf, Security, JPA, MySQL, H2, Mail, Facebook, Twitter, LinkedIn and Actuator items checked, and added some extras aftwerwards</p>
  
  <p>Using Java 7 and Tomcat 7 because i intend to deploy the project on Openshift in a near future, and next there are my config files: </p>
  
  <p>pom.xml</p>
  
  <pre><code>&lt;parent&gt;
      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
      &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
      &lt;version&gt;1.2.3.RELEASE&lt;/version&gt;
      &lt;relativePath/&gt;
  &lt;/parent&gt;
  &lt;properties&gt;
      &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
      &lt;start-class&gt;com.adrisasws.springmvc.WebApplication&lt;/start-class&gt;
      &lt;java.version&gt;1.7&lt;/java.version&gt;
      &lt;tomcat.version&gt;7.0.59&lt;/tomcat.version&gt;
  &lt;/properties&gt;
  &lt;dependencyManagement&gt;
      &lt;dependencies&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;io.spring.platform&lt;/groupId&gt;
              &lt;artifactId&gt;platform-bom&lt;/artifactId&gt;
              &lt;version&gt;1.1.2.RELEASE&lt;/version&gt;
              &lt;type&gt;pom&lt;/type&gt;
              &lt;scope&gt;import&lt;/scope&gt;
          &lt;/dependency&gt;
      &lt;/dependencies&gt;
  &lt;/dependencyManagement&gt;
  &lt;dependencies&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.thymeleaf.extras&lt;/groupId&gt;
          &lt;artifactId&gt;thymeleaf-extras-springsecurity3&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;com.h2database&lt;/groupId&gt;
          &lt;artifactId&gt;h2&lt;/artifactId&gt;
          &lt;scope&gt;runtime&lt;/scope&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;mysql&lt;/groupId&gt;
          &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
          &lt;scope&gt;runtime&lt;/scope&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-mail&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.social&lt;/groupId&gt;
          &lt;artifactId&gt;spring-social-security&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-social-facebook&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-social-linkedin&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-social-twitter&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.social&lt;/groupId&gt;
          &lt;artifactId&gt;spring-social-google&lt;/artifactId&gt;
          &lt;version&gt;1.0.0.RELEASE&lt;/version&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt;
          &lt;artifactId&gt;httpclient&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;
          &lt;scope&gt;provided&lt;/scope&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
          &lt;scope&gt;test&lt;/scope&gt;
      &lt;/dependency&gt;
  &lt;/dependencies&gt;
  &lt;build&gt;
      &lt;plugins&gt;
          &lt;plugin&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
          &lt;/plugin&gt;
      &lt;/plugins&gt;
  &lt;/build&gt;
  &lt;profiles&gt;
      &lt;profile&gt;
          &lt;id&gt;openshift&lt;/id&gt;
          &lt;build&gt;
              &lt;finalName&gt;webapp&lt;/finalName&gt;
              &lt;plugins&gt;
                  &lt;plugin&gt;
                      &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
                      &lt;version&gt;2.1.1&lt;/version&gt;
                      &lt;configuration&gt;
                          &lt;outputDirectory&gt;webapps&lt;/outputDirectory&gt;
                          &lt;warName&gt;ROOT&lt;/warName&gt;
                      &lt;/configuration&gt;
                  &lt;/plugin&gt;
              &lt;/plugins&gt;
          &lt;/build&gt;
      &lt;/profile&gt;
  &lt;/profiles&gt;
  </code></pre>
  
  <p>Security config (exactly the same security file i'm using in a non-boot project in which the CSRF token actually gets injected automatically) </p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  
      //////////////////////////////////////////////////////////////////////////
      //                              DEPENDENCIES                            //
      //////////////////////////////////////////////////////////////////////////
  
      @Autowired private DataSource dataSource;
      @Autowired private UserRepository userRepository;
  
  
      //////////////////////////////////////////////////////////////////////////
      //                               PROPERTIES                             //
      //////////////////////////////////////////////////////////////////////////
  
      @Value("${custom.security.rememberme-secret}")  private String secret;
      @Value("${custom.security.rememberme-create-tables}") private String createTables;
  
      private final static String[] adminRequests = new String[] { ... some matchers here... };
      private final static String[] userRequests = new String[] { ... some matchers here... };
      private final static String[] publicRequests = new String[] { ...some matchers here... };
  
  
      //////////////////////////////////////////////////////////////////////////
      //                              AUTHORIZATION                           //
      //////////////////////////////////////////////////////////////////////////
  
      @Override
      public void configure(WebSecurity web) throws Exception {
          web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/error**");
      }
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http
              .authorizeRequests()
                  .antMatchers(adminRequests).access("hasRole('"+Role.ADMIN.toString()+"')")
                  .antMatchers(userRequests).access("hasRole('"+Role.USER.toString()+"')")
                  .antMatchers(publicRequests).permitAll()
                  .anyRequest().authenticated()
                  .and()
              .requiresChannel()
                  .anyRequest().requiresSecure()
                  .and()
              .formLogin()
                  .loginPage("/login")
                  .defaultSuccessUrl("/", false)
                  .permitAll()
                  .and()
              .logout()
                  .logoutUrl("/logout")
                  .logoutSuccessUrl("/login?logout")
                  .invalidateHttpSession(true)
                  .deleteCookies("JSESSIONID")
                  .permitAll()
                  .and()
              .rememberMe()
                  .rememberMeServices(rememberMeService())
                  .and()
              .apply(new SpringSocialConfigurer());
      }
  
  
      //////////////////////////////////////////////////////////////////////////
      //                              AUTHENTICATION                          //
      //////////////////////////////////////////////////////////////////////////
  
      @Override
      public void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth
              .userDetailsService(userDetailsService())
              .passwordEncoder(bCryptPasswordEncoder());
      }
  
      @Bean
      public BCryptPasswordEncoder bCryptPasswordEncoder() {
          return new BCryptPasswordEncoder(11);
      }
  
      @Bean
      public UserDetailsService userDetailsService() {
          return new UserRepositoryUserDetailsService(userRepository);
      }
  
      @Bean
      public SocialUserDetailsService socialUserDetailsService() {
          return new UserRepositorySocialUserDetailsService(userDetailsService());
      }
  
  
      //////////////////////////////////////////////////////////////////////////
      //                               REMEMBER ME                            //
      //////////////////////////////////////////////////////////////////////////
  
      @Bean
      public JdbcTokenRepositoryImpl jdbcTokenRepository() {
          JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
          jdbcTokenRepository.setDataSource(dataSource);
          jdbcTokenRepository.setCreateTableOnStartup(Boolean.valueOf(createTables));
          return jdbcTokenRepository; 
      }
  
      @Bean
      public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
          return new RememberMeAuthenticationProvider(secret);
      }
  
      @Bean 
      public PersistentTokenBasedRememberMeServices rememberMeService() {
          PersistentTokenBasedRememberMeServices service = 
                  new PersistentTokenBasedRememberMeServices(secret, userDetailsService(), jdbcTokenRepository());
          service.setUseSecureCookie(true);
          service.setParameter("rememberme");
          service.setTokenValiditySeconds(AbstractRememberMeServices.TWO_WEEKS_S);
          return service;
      }
  
      @Bean
      public RememberMeAuthenticationFilter authenticationFilter() throws Exception {
          return new RememberMeAuthenticationFilter(authenticationManager(), rememberMeService());
      }
  }
  </code></pre>
  
  <p>in my spring boot configt at the moment related to thymeleaf, and for development purposes</p>
  
  <pre><code>spring.thymeleaf.cache=false
  </code></pre>
  
  <p>and thymeleaf templates look like this (my login page at the moment, will include only the relevant content for the sake of clarity) </p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security/"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="thymeleaf/layouts/default"&gt;
  &lt;head&gt;
      ... css and meta tags ...
  &lt;/head&gt;
  &lt;body&gt;
          ... some html ...
          &lt;th:block sec:authorize="isAnonymous()"&gt;
          &lt;!-- Bad Credentials --&gt;
          &lt;div th:if="${param.error}" class="alert alert-danger text-center"&gt;
              Invalid username and/or password.
          &lt;/div&gt;
          &lt;!-- Logout --&gt;
          &lt;div th:if="${param.logout}" class="alert alert-success text-center"&gt;
              You have been logged out.
          &lt;/div&gt;
  
          &lt;!-- Login Form --&gt;
          &lt;form id="f" th:action="@{/login}" method="post" role="form" autocomplete="off"&gt;
              &lt;!-- Username --&gt;       
              &lt;input type="text" class="form-control text-center" id="username" name="username" th:placeholder="#{form.login.username}" /&gt;
              &lt;!-- Password --&gt;
              &lt;input type="password" class="form-control text-center" id="password" name="password" th:placeholder="#{form.login.password}" /&gt;
              &lt;!-- Remember me --&gt;
              &lt;input type="checkbox" id="rememberme" name="rememberme" /&gt;
              &lt;!-- Submit --&gt;
              &lt;button type="submit" class="btn btn-primary" th:utext="#{form.login.submit}"&gt;Login&lt;/button&gt;
              &lt;input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /&gt;
          &lt;/form&gt;
          ... more html and javascript ...
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p><strong>Edit2</strong> - after doing some debugging in the direction <a href="https://stackoverflow.com/users/1251350/faraj-farook">Faraj Farook</a> pointed, i found out that, in a project with the configuration i posted, in the Spring Boot version, in this class <code>org.thymeleaf.spring4.requestdata.RequestDataValueProcessor4Delegate</code>, the following function returns a null processor</p>
  
  <pre><code>public Map&lt;String, String&gt; getExtraHiddenFields(
          final RequestContext requestContext, final HttpServletRequest request) {
  
      final RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor();
      if (processor == null) {
          return null;
      }
  
      return processor.getExtraHiddenFields(request);
  
  }
  </code></pre>
  
  <p>whereas the non Spring boot version, it returns a processor which is an instance of <code>org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor</code>.</p>