Announcement Announcement Module
Collapse
No announcement yet.
Can't get CSRF token working - Integrate Spring Security with Thymeleaf + No SS Auth Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Can't get CSRF token working - Integrate Spring Security with Thymeleaf + No SS Auth

    I have been trying for the past week or so to integrate Spring Security 3.2.3 into our Spring 3.2.8 application, but ran into several roadblocks. I am a newbie to Spring itself so forgive me for some basic questions. Our setup is non-standard so I'm not sure if getting CSRF token functionality is actually possible with these following requirements:

    The first problem is that I can't get Spring Security to actually run with our current application since we already use external authentication (it's an entirely custom auth API) and I don't need any of the SS auth functionality, so I want to SS to ignore that somehow. The application refuses to even load if you don't specify an authenticationmanager, but adding a user seems to not work either since it tries to authenticate using that instead...a nice catch-22. Here's what my spring-security.xml looks like:

    Code:
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-3.2.xsd">
    
        <http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
            <csrf />
        </http>
    
        <authentication-manager>
            <authentication-provider>
              <user-service>
                <user name="username" password="password" authorities="ROLE_USER, ROLE_ADMIN" />
              </user-service>
            </authentication-provider>
          </authentication-manager>
        
        <beans:bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint">
        </beans:bean>
     
    </beans:beans>
    Next issue is that I am not doing a form submit, but AJAX POST, so I need to go with "place the token in the http header" route. This means that even if a hidden input field is automatically placed into my page with the CSRF token, I can't use it. I'll need to put the token in manually in the page's meta tags, which leads me to my last problem:

    We have Thymeleaf in place as our template engine, which makes it difficult since JSPs are not used, only plain .html files with th: tags for evaluating expressions. This means that placing a CSRF token into my page, like this:

    <meta name="_csrf" content="${_csrf.token}"/>

    doesn't work since .html won't evaluate that, and a th: tag just errors out on it:
    Exception evaluating SpringEL expression: "_csrf.token"
    So the last question is: "how do I allow the use of JSPs in tandem with .html files in Thymeleaf?"

    I tried the below in our spring-servlet.xml to attempt to exclude JSPs from being loaded by Thymeleaf, but get the error:

    Problem accessing {my path} Reason:

    org.eclipse.jetty.webapp.WebAppContext$Context.get JspConfigDescriptor()Ljavax/servlet/descriptor/JspConfigDescriptor;
    Code:
        <bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
            <property name="templateEngine" ref="templateEngine" />
            <property name="characterEncoding" value="UTF-8" />
            <property name="viewNames" value="*.html" />
            <property name="excludedViewNames">
                <array>
                  <value>*.jsp</value>
                </array>
            </property>
        </bean>
        
    
        <bean id="templateResolver" class="org.thymeleaf.templateresolver.FileTemplateResolver">
            <property name="order" value="5" />
            <property name="templateMode" value="HTML5" />
            <property name="characterEncoding" value="UTF-8" />
            <property name="cacheable" value="false" />
        </bean>
        
         <bean id="viewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
               <property name="viewNames">
                   <value>*.jsp</value>
               </property>
                <property name="order" value="6" />
         </bean>
    If anyone has any suggestions, it would make my day.
    Last edited by starmandeluxe; May 22nd, 2014, 09:56 PM.

  • #2
    The first problem is that I can't get Spring Security to actually run with our current application since we already use external authentication (it's an entirely custom auth API) and I don't need any of the SS auth functionality, so I want to SS to ignore that somehow. The application refuses to even load if you don't specify an authenticationmanager, but adding a user seems to not work either since it tries to authenticate using that instead...a nice catch-22. Here's what my spring-security.xml looks like:
    If you are using Spring Security for authorization, it still needs to know what roles your user has to make access control decisions. Have a look at the Pre-Authentication scenarios http://docs.spring.io/spring-securit...ingle/#preauth


    We have Thymeleaf in place as our template engine, which makes it difficult since JSPs are not used, only plain .html files with th: tags for evaluating expressions. This means that placing a CSRF token into my page, like this:

    <meta name="_csrf" content="${_csrf.token}"/>
    Did you try:

    Code:
     <meta name="_csrf" value="dummy" th:value="${_csrf.token}"/>

    Comment


    • #3
      Thank you so much for the advice, Rob. For the authorization problem, I somehow got around it by using "http403EntryPoint" in the configuration: http://stackoverflow.com/questions/8...tionentrypoint


      I did try the th:value but got the error
      Exception evaluating SpringEL expression: "_csrf.token"
      I struggled with that for a while, and almost considered doing away with Thymeleaf just so that I could use a normal JSP to evaluate the CSRF token, but eventually I solved the problem. It was hidden away in the annals of the Thymeleaf forums:

      https://github.com/thymeleaf/thymele...mment-27643488

      Infamous Issue 7. The last comment by the creator of Thymeleaf says that:

      th:action ... detects when this attribute is being applied on a <form> tag --which should be the only place, anyway--, and in such case callsRequestDataValueProcessor.getExtraHiddenFields(... ) and adds the returned hidden fields just before the closing </form> tag.
      That was the key phrase I needed to get the token to work. Unfortunately it's completely unobvious why "th:action" would also kick off getExtraHiddenFields, but at any rate it does, and that's what matters.

      So for anyone struggling with Thymeleaf + Spring Security CSRF + AJAX POST, here are my steps:

      1. Implement the Spring interface RequestDataValueProcessor and register it in Spring Security's XML config so you can override the method getExtraHiddenFields, which allows you to insert a hidden input field into the HTML (with the token of course). The token itself is generated with a Java.Util UUID.

      2. With JQuery, read the value from that hidden field and set the Request Header's "X-CSRF-Token" attribute so that it gets sent over HTTP. It's not possible to simply leave the token in the hidden input field because we are not doing a form Submit, instead we use AJAX POST to call methods on the server side.

      3. Extend Spring's HandlerInterceptorAdapter and register it as an interceptor so that every time a POST method is done, the "preHandle" method on the server side is called so it can compare the request token (extracted from the HTTP header in the previous step) to the session's token (should be the same!). After it does this check, it can either allow the request to go through or return an error.

      Paraphrased from Eyal Lupu's great blog post here, but I had to tweak it to my situation because of my AJAX requirement. Funnily enough his post is linked to as part of the official Thymeleaf documentation.

      Good luck!

      Comment

      Working...
      X