Brandon Wagner
Brandon Wagner

Reputation: 893

Spring Boot Groovy Templates Not Adding _csrf to model

I have a very simple Spring Boot Application which consists of a main Application.java (with the default main method), a MainController (which has one requestMapping to /login), and a SecurityConfig (with mainly default values).

My problem is with the _csrf support in Groovy Templates. Everything works fine with FreeMarker, but when I switch to GroovyTemplates the _csrf param does not get put into the model.

Is there a bug in the Groovy Templates, something manually I have to do to grab the token, or is there some configuration step I'm missing (although I don't know why it would work for FreeMarker) ?

UPDATE:

I printed this.properties (HashMap) on the login.tpl (Groovy Template) :

{class=class login, out=java.io.BufferedWriter@5e2aead3, model={error=Optional.empty, org.springframework.validation.BindingResult.error=org.springframework.validation.BeanPropertyBindingResult: 0 errors, spring=org.springframework.web.servlet.support.RequestContext@1d99fb33, springMacroRequestContext=org.springframework.web.servlet.support.RequestContext@7fcc5c78}}

The model key in the properties map includes the parameters

I added error in the Controller action using the:

@RequestMapping(value="/login", method = RequestMethod.GET)
public ModelAndView login(@RequestParam Optional<String> error) {
    return new ModelAndView("views/login", "error", error);
}

Upvotes: 1

Views: 676

Answers (2)

chubbsondubs
chubbsondubs

Reputation: 38676

So the hacked up way to handle this is to manually add the _csrf attribute to your model in your Controller. For example:

model.addAttribute( "_csrf", request.getAttribute("_csrf") );

I wouldn't recommend that if you have lots of views on your server. I'd suggest you follow one of the options from @M-Deinnum. But, for quick testing this works.

Upvotes: 0

M. Deinum
M. Deinum

Reputation: 124441

When using the GroovyMarkupView and the GroovyMarkupViewResolver the properties of the view only contain properties available in the model (and some added for Groovy).

To include the request attributes set the exposeRequestAttributes property of the GroovyMarkupViewResolver to true. Ideally this is done by setting the following property in the application.properties.

spring.groovy.template.exposeRequestAttributes=true

However due to this issue that currently isn't possible.

To work around it create a BeanPostProcessor which check if the incoming bean is a GroovyMarkupViewResolver (or AbstractTemplateViewResolver if you want a more general approach). If so set the exposeRequestAttributes to true.

public class TemplateViewResolverPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        if (bean instance GroovyMarkupViewResolver) {
            ((GroovyMarkupViewResolver) bean).setExposeRequestAttributes(true);
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

After doing that the CsfrToken is available with the key _csfr, be aware that this is the actual CsfrToken.

Another solution is to create a HandlerInterceptor implement the postHandle method and add the _csfr property to the model. That way you can simply add the value of the token instead of the actual token itself. This will work with any view technology used.

public class CsrfAddingInterceptor extends HandlerInterceptorAdapter {

    public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mav) throws Exception {
        CsrfToken token = (CsrfToken) req.getAttribute(CsrfToken.class.getName())
        if (token != null) {
            mav.addAttribute(token.getParameterName(), token.getToken());
        }
    }

}

Then add it as an interceptor and you will have the value available.

Upvotes: 4

Related Questions