11

All my $.ajax, both POST and GET were working fine, but as soon as I integrated Spring security 3.2.6 into my project the POST ajax requests stopped working without loggin any issues.

spring-security.xml

<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://www.springframework.org/schema/security"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <!--Permit all Web resources to bypass proxy-->
    <http pattern="/js/**" security="none"/>
    <http pattern="/css/**" security="none"/>
    <http pattern="/fonts/**" security="none"/>
    <http pattern="/images/**" security="none"/>

    <http auto-config="true" use-expressions="true" >

        <intercept-url pattern="/login" access="isAnonymous()"/>

        <intercept-url pattern="/workflow**" access="hasRole('ROLE_WORKFLOW_ADMIN')"/>
        <intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMIN','ROLE_WORKFLOW_ADMIN','ROLE_DMS_ADMIN')"/>

        <access-denied-handler error-page="/403"/>

        <form-login
                login-page="/login"
                default-target-url="/dashboard"
                authentication-failure-url="/login?error"
                username-parameter="username"
                password-parameter="password"/>

        <logout invalidate-session="true" logout-success-url="/login?logout"/>

        <csrf/>
    </http>

    <!-- Select users and user_roles from database -->
    <authentication-manager>
        <authentication-provider ref="daoAuthenticationProvider"/>
    </authentication-manager>

    <beans:bean id="daoAuthenticationProvider"
                class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="userDetailsService" ref="authService"/>
    </beans:bean>


</beans:beans>

Web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

    <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/mvc-dispatcher-servlet.xml
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/error</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error</location>
    </error-page>

</web-app>

Edit


The URL I am trying to access is

http://localhost:8080/ADMIN/workflow/sample-ajax

Could it be something to with spring security ?

Mushtaq Jameel
  • 7,053
  • 7
  • 33
  • 52
  • Please share the URL that you are hitting from the ajax request. – Mithun Mar 11 '15 at 11:54
  • _POST ajax requests stopped_...not a demonstrative problem statement. But are there in error in the console. it could be possible your url might have changed a little. – Jai Mar 11 '15 at 11:54
  • @Mithun - See my Edit above – Mushtaq Jameel Mar 12 '15 at 06:06
  • Based on the configuration, URL's matching `/workflow**` should have `ROLE_WORKFLOW_ADMIN` role. Could you please confirm if the user that you are using belongs to this role? – Mithun Mar 12 '15 at 06:12

4 Answers4

15

Finally after three agonizing days, I found the problem and boy was it stupid.

The problem was that I have enabled csrf protection in spring security. And that was causing my post requests to be forbidden which triggers the access-denied-handler error page, since I have not mapped my access-denied-handlerto the "/403" error page as shown below, my http 403/401 was being masked by the http 404

<access-denied-handler error-page="/403"/>

So in Short

  1. Map your access-denied-handler error page to a valid url
  2. If you use csrf protection, then always make sure that you pass them in the ajax post request as such

$.ajax({method :'POST', url : '/ajax',data : {"${_csrf.parameterName}" : "${_csrf.token}"}});

Mushtaq Jameel
  • 7,053
  • 7
  • 33
  • 52
5

There is one more situation that must be consulted specially when you are implementing spring security 4, one needs form button (not "a href").

https://spring.io/blog/2013/08/21/spring-security-3-2-0-rc1-highlights-csrf-protection/#ajax-requests.

A> form post request sent: input type hidden in both login/logout buttons

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

B> For AJAX post request add following in your JSP page after taglib declarations.

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

and some where above closing body tag in jsp any where as it "on ready"

<script type="text/javascript">
$(function() {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});
 </script>

It should work! if you have CORS issues still remaining, see article below. http://www.html5rocks.com/en/tutorials/cors/

vimal krishna
  • 2,886
  • 28
  • 22
1

Alternative solution using Spring Boot 2.2.1 with Spring MVC, Spring Security 5 and Thymeleaf 3.0.11

I am so happy that I finally found a solution to this problem, that I would also like to share it here:

Problem:

In my case, it was a $.ajax POST request to a valid URL returning 404 error status (not found).

@Mushtaq Jameel has explained that the original cause of the problem is csrf, which is enabled by default as of Spring Security 4 (source).

Solution:

I have not tested what @Mushtaq Jameel proposed, but this elegant quick fix worked for me:

AJAX code:

  $.ajax({
                type: "POST",
                url: "/",
                data: $('.cd-signin-modal__form.sign-up').serialize(),  // <- FIX    
                success: // some code
                error: // some code
            });

        }

In other words, the solution is calling the .serialize() method on the HTML form itself.

What happens then, is that a _csfr token is automatically added in the POST request as an additional form parameter:

enter image description here

This is again due to csrf being enabled by default in the newer versions of Spring Security (here it is mentioned that this hidden form parameter is added automatically).

My Spring MVC controller then accepts the form like this:

 @PostMapping("/")
    public String createUser(@Valid @ModelAttribute User user, @Valid @ModelAttribute UserDetails userDetails, BindingResult errors) {
        if (errors.hasErrors()) {
            // handle errors
        }
        //  persist objects in database
        return "index";
    }
Petar Bivolarski
  • 1,599
  • 10
  • 19
0

Have you tried to post using normal http request for using spring security you have to use particular set of variable name you can google it .

Also check if we can send post request to restricted URL using AJAX.

  • @himashu - I did it works just fine also the GET requests don't seem to be having a problem with the ajax call. What do you mean by particular set of variable name, are you talking about j_username & j_password, aren't those things for login ? – Mushtaq Jameel Mar 13 '15 at 08:37