6

How to disable CSRF in Spring Security 4 only for specific URL pattern through XML configuration?

Spring-security.xml

<security:http auto-config="true" use-expressions="true" pattern="/ext/**">
    <csrf disabled="true" />
</security:http>

<security:http auto-config="true" use-expressions="true" authentication-manager-ref="authenticationManager">
    <security:intercept-url pattern="/auth/**" access="hasAnyRole('ROLE_USER')" />
    <security:form-login login-page="/login" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-url="/login" login-processing-url="/j_spring_security_check" />
    <security:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
</security:http>

My code works fine if I use only one security:http block, but after I add another block it throws error as below:

Error

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter gov.in.controller.filter.LoginAdtAuthFailHdlr.usernamePasswordAuthenticationFilter; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter] is defined: expected single matching bean but found 2: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#1
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 58 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter] is defined: expected single matching bean but found 2: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#1
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 60 more
ad-inf
  • 1,520
  • 4
  • 30
  • 53

2 Answers2

14

Could not achieve with just XML changes. Below worked for me

Change in Spring-security.xml

<security:http  use-expressions="true" authentication-manager-ref="authenticationManager">
    <security:intercept-url pattern="/auth/**" access="hasAnyRole('ROLE_USER')" />
    <security:form-login login-page="/login" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-url="/login" login-processing-url="/j_spring_security_check" />
    <security:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler" />

    <security:csrf request-matcher-ref="csrfSecurityRequestMatcher"  />
</security:http>

CsrfSecurityRequestMatcher

public class CsrfSecurityRequestMatcher implements RequestMatcher {
    private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
    private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("/ext/**", null);

    @Override
    public boolean matches(HttpServletRequest request) {          
        if(allowedMethods.matcher(request.getMethod()).matches()){
            return false;
        }
        return !unprotectedMatcher.matches(request);
    }
}
Vik David
  • 3,640
  • 4
  • 21
  • 29
ad-inf
  • 1,520
  • 4
  • 30
  • 53
5

You can have two (or more) filter chains:

<http pattern="/your-specific/**">
  <!-- ... -->
  <csrf disabled="true"/>
</http>
<http>
  <!-- ... -->
</http>
holmis83
  • 15,922
  • 5
  • 82
  • 83
  • Thanks for your response. I added some xml and error above. when i add second http block it is throwing duplicate bean errors. Any suggestion? – ad-inf Aug 12 '15 at 12:18
  • @ad-inf You have a reference to UsernamePasswordAuthenticationFilter in your `LoginAdtAuthFailHdlr`. With two `form-login` you get two `UsernamePasswordAuthenticationFilter`, you need to change your class or remove one form-login. – holmis83 Aug 12 '15 at 14:15
  • tried this too. removed all elements but error is still same. Updated xml – ad-inf Aug 12 '15 at 14:21
  • @ad-inf `auto-config="true"` is shorthand for `form-login` and more (see reference doc), I recommend not to use it. – holmis83 Aug 12 '15 at 14:30
  • ' 'worked with other – ad-inf Aug 13 '15 at 01:22
  • thanks for your pointers. I could disable CSRF by adding a bean as detailed in my answer below. If there is better solution, please let me know – ad-inf Aug 14 '15 at 08:32