Johan Basson Johan Basson - 1 year ago 166
C# Question

Spring Boot, Freemarker, MVC Unit Tests, Csrf

Im using Freemarker with Spring Boot and do mvc unit tests. In my freemarker template I have a hidden input field for the csrf token like this:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

Then I also have a mvc unit test :

@SpringApplicationConfiguration(classes = MyApplication.class)
public class MvcTests {

WebApplicationContext ctx;

MockMvc mvc;

HttpSessionCsrfTokenRepository httpSessionCsrfTokenRepository;

public void setUp() throws Exception {
mvc = webAppContextSetup(ctx).build();
httpSessionCsrfTokenRepository = new HttpSessionCsrfTokenRepository();

public void testInValidVoucherNumber() throws Exception {
CsrfToken csrfToken = httpSessionCsrfTokenRepository.generateToken(new MockHttpServletRequest());
.sessionAttr("_csrf.parameterName", "_csrf")
.sessionAttr("_csrf.token", csrfToken.getToken())
.param("code", "ABC")
.param("_csrf", csrfToken.getToken()))
.andExpect(content().string(containsString("Code is invalid")));

When I run the unit test I get the following freemarker error:

testInValidVoucherNumber(MvcTests) Time elapsed: 0.904 sec <<< ERROR!
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> _csrf [in template "claim-voucher.ftl" at line 25, column 34]

Tip: If the failing expression is known to be legally refer to something that's null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (!myDefault, (

FTL stack trace ("~" means nesting-related):
- Failed at: ${_csrf.parameterName} [in template "claim-voucher.ftl" at line 25, column 32]
~ Reached through: #nested [in template "layout.ftl" in macro "basic" at line 16, column 9]

Here is the controller:

@RequestMapping(value = "/voucher/claim", method = RequestMethod.POST)
public String checkVoucherCode(@Valid ClaimVoucherForm claimVoucherForm, Model model) {
//Business logic

It seems like freemarker does not have access to the csrf parameterName and csrf token. Is there a way to include the csrf information without changing the controller or is there a problem with the unit test?

Answer Source

Instead of:

.sessionAttr("_csrf.parameterName", "_csrf")
.sessionAttr("_csrf.token", csrfToken.getToken())


.sessionAttr("_csrf", csrfToken)

When you write ${_csrf.parameterName}, FreeMarker looks for the field "parameterName" on the model variable "_csrf". Not for the model variable "_csrf.parameterName".

That said, it's weird that you have to add those parameters as session attributes in your test. Shouldn't Spring Security's CsrfFilter do that for you?