I have built a REST API backend whith Spring MVC and secured with basic Auth with Spring Security.
I would like to do cross domain ajax call to the REST API from Javascript clients. I don't want to use JSONP because i don't want to be limited to GET calls. I use CORS and i have put the right headers on server side.
Suppose my REST API is on domain localhost:8087 and my client on localhost:8086, which is cross domain call.
In my Javascript client, i make ajax call with jQuery :
<script>
$.ajax ({
url: "http://localhost:8087/SpringMVC/users/user1",
beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", "Basic xxxxxxxxxxxx"); },
success: function(val) { console.log(val); alert("success" + val); },
error: function(val) { console.log(val); alert("error" + val); }
});
</script>
My problem is that jQuery does not send the Authorization header in the HTTP request and i don't know why. I don't understand because i do it in the beforeSend method, so it should be inside the HTTP request. Result : i have a 401 error.
When i try the script from the same domain localhost:8087, which is not cross domain anymore, i have no problem.
How is it possible ?
My script is just a test. I don't intend to put my username/password on client side. But i want to test how to do ajax calls to a basic auth protected REST API. I imagine i have to send on server side to be secure my username/password, the REST API sends me back a cookie and i don't need to pass the username/password anymore for my next ajax calls to the REST API. Am i right ?
I have tested my REST API with Chrome Advanced REST client and it is working like that. For the first request i need to pass the authorization header. Then it is not needed. Should it work also like that with my javascript web client ? I intend to use Node.JS with Backbone to build it.
Thanks a lot.
EDIT2 : Seems really to be a CORS Browser problem. I have added the header Access-Control-Allow-Methods for OPTIONS method on server side and it works on Chrome. I have access to the JSON response with no error anymore. But i still need to use the authorization header for the next requests. How to tell jQuery to use the cookie sent ?
And when i try with Firefox 11, i have no access to the json response and i have the error :
"NetworkError: 401 Non-Autorisé - http://localhost:8087/SpringMVC/users/user1"
Apparently, Chrome and Firefox treat Cross Domain requests a bit differently. Before doing the cross domain request, they do what is called a 'preflight' request with HTTP OPTIONS method. The difference between Chrome and Firefox is that Chrome sends also the Authorization header with the credentials whereas Firefox do not.
Then, it remains a Spring Security config problem. My url /users/* is secured for all HTTP methods, including OPTIONS. In the case of Firefox, as the Authorization header is not sent, my request is not authorized. If i restrict my secure url /users/* to the GET method only, then it perfectly works for Firefox. So i had to add only that in my Spring security config :
<intercept-url pattern="/users/*" access="isAuthenticated()" method="GET"/>
Afterwards, i have the choice : i can add other methods to be secured in the intercept-url, except OPTIONS, or i can restrict the HTTP method call to GET in my Spring MVC controller, which will even treat my OPTIONS calls according to the Javadoc. I chose the second solution. But if somebody finds a solution to force Firefox to send the credentials like Chrome, it would be great and i would chose this one.
Another option for the Spring Security configuration scenario presented by rico would be:
<http ... use-expressions="true">
<intercept-url pattern="/users/*" access="permitAll" method="OPTIONS"/>
<intercept-url pattern="/users/*" access="isAuthenticated()"/>
...
</http>
HTTP OPTIONS requests will always pass through authentication and any other HTTP method will not.
Note the use-expressions
XML attribute set to true in <http>
element. Spring Security will then expect the access
attributes of the <intercept-url>
elements to contain Spring EL expressions, like permitAll and isAuthenticated().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With