When submitting information via an HTML form, Spring Security requires the form to include a CSRF (Cross-Site Request Forgery) token. This token is generated by the Spring app and tied to the user's session. Spring will use it to verify that the form submission is legitimate and not malicious. The token is required on POST, PUT, DELETE, and PATCH requests to protect against CSRF attacks. So when your user is submitting information, the form used to submit said information will need to contain a valid csrf token. This means adding a hidden field to the form that contains the token. In this post I'll demonstrate my reusable solution to this when using Spring and JTE.

Adding the hidden field

In the below example logout form jte template, the csrf param is used to create the hidden field. In this scenario the controller will be required to set the csrf param as a model attribute.

@import org.springframework.security.web.csrf.CsrfToken

@param CsrfToken csrf

<form action="/logout" method="post">
    <input type="hidden" name="${csrf.parameterName}" value="${csrf.token}"/>
    <input type="submit" value="Log out" />
</form>

But the question is, how can I make it easy to add this to all my forms? And how can I do it without having to pass the token to the view in each controller method?

The solution is to create a JTE template that can be included in all forms. This template will read the csrf token, which will be supplied via a @ControllerAdvice annotated class on each request. The result will be a JTE template that can be used like so:

@import org.springframework.security.web.csrf.CsrfToken

@param CsrfToken csrf

<form action="/logout" method="post">
    @template.components.csrf(csrf = csrf)
    <input type="submit" value="Log out" />
</form>

Implementation

To make the csrf component work without passing the csrf token to each view model, we will create a class with a @ControllerAdvice annotation. This will allow us to annotate a method with @ModelAttribute to set the csrf param.

@ControllerAdvice
public class CsrfControllerAdvice {
    @ModelAttribute
    public void addCsrfToken(Model model, CsrfToken token) {
        model.addAttribute("csrf", token);
    }
}

The csrf jte template will look as follows.

@import org.springframework.security.web.csrf.CsrfToken

@param CsrfToken csrf

@if(csrf != null)
    <input type="hidden" name="${csrf.getParameterName()}" value="${csrf.getToken()}">
@endif

The one downside with this is we will still have to pass the csrf value to the template as a parameter. The upside is the controller does not have to be updated to add the model attribute due to the CsrfControllerAdvice class.