CORS with AngularJS and Spring REST

If you are developing RESTFul webservice producing JSON as a response that may be consumed by clients, you need to aware of the same-origin policy. The same-origin policy is the important security concept implemented by browser to prevent javascript codes from sending request to different source. The same-origin policy not only prevent from sending request to different origin but also prevent from sending request to different port and different protocols.

Cross Origin Resource Sharing – CORS– is a technique that permits that kind of situation.  It allows the clients to communicate servers from different origin. For example, when abc.com requests some resources from def.com, same-origin policy usually block the request.  By supporting CORS, the server can include a special header in the response to allows clients to access data.

Normally, clients start CORS request by making HTTP methods to access resources on a server. The request usually have origin header which indicates the origin of the request to let the servers to decide if the request should be permitted.  Server will respond to the client with Access-Control-Allow-Origin header to let clients know which client origin can access the requests on the server. If the Access-Control-Allow-Origin is missing in the response or doesn’t match the origin, the browser doesn’t allow the request.

According to same-origin policy and CORS, if you want all clients or only permitted clients to be able to access the resources  on the server, you should set the Access-Control-Allow-Origin header in the response. Let’s see how we implements this scenario with AngularJS and Spring RESTFul web service.

Spring RESTFul Webservice

In Spring MVC, we can easily produce JSON with the help of Jackson Json message converter. My webservice controller will look like following. It simply produces JSON instead of returning logical view name. In order to produce Json, we need to annotate @ResponseBody annotation. That annotation will tell Spring to convert the array to Json with the help of Jackson.

@Controller
public class CountriesController {

	@RequestMapping(value="/cities", method=RequestMethod.GET)
	@ResponseBody
	public String[] cities(HttpServletResponse response){
		String[] cities={"New York","London","Amsterdam","Paris","Milan","Madrid","Sydney","Tokyo","Singapore","Yangon","Rio","Vegas"};

                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
                response.setHeader("Access-Control-Max-Age", "3600");
                response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

                return cities;
	}
}

Spring will map any GET request to /cities with cities() method.  response.setHeader(“Access-Control-Allow-Origin”, “*”); This line set the Access-Control-Allow-Origin header to the response allowing all client origin can access the data. Practically, it’s not safe unless you want to public your resources. But in this situation, it’s enough for demonstration.

I will host the Spring MVC app on localhost and try to access from different origin. Nothing much to say, I will write javascript codes which is not part of the application to access the resources.

AngularJS $http service

I will use AngularJS $http service to send ajax request to /cities. AngularJS is popular JS MV* framework made by internet giant Google.

var CityModule=angular.module("City",[]);

			CityModule.factory("CityRetriever",function($http){
				var City={};

				(function(){
					$http.get('http://localhost:8080/demo/cities').success(function(data,status){

								if(typeof data=='object'){
									console.log("Response data : "+data);
									City.names=data;
								}

							}).error(function(){
								alert("Failed to access");
							})
				})();

				return City;
			});

			CityModule.controller("CityController",function($scope,CityRetriever){
				$scope.cities=CityRetriever;
			});

First, I create a angular module called City. That module encapsulates the functionalities that are required to fetch data from the server. Then, I create service called CityRetriever by using Angular’s factory method. At the end, I create angular controller named CityController to invoke CityRetriever service and bind the model to $scope.  I fetch the data by using $http service method, get(). This method is used to send get request to http://localhost:8080/demo/cities. The above code is really self-explanatory. $http.get() sends GET request to http://localhost:8080/demo/cities and fetch the data. If succeed, bind the response data as a City object property.

In the controller, by calling the service name CityRetriever, the service will return City object as written in the factory method and bind the City object to $scope with the property cities. Later, the data can be rendered on html page by processing $scope.cities.

Let’s remove the following code from the Spring REST controller, buid the Spring MVC application and run.

                response.setHeader("Access-Control-Allow-Origin", "*");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		response.setHeader("Access-Control-Max-Age", "3600");
		response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

Then, try to access by sending the request from the javascript code, you will get the error below in the browser developer tools console.

cors-error
Remember? It’s because of same-origin policy. Browser disallow the request anymore. But put the Access-Control-Allow-Origin back in response header in Spring controller, you will see the error will be gone. This way you can restrict the clients from accessing the resources on the server.

Adding CORS Filter to Spring MVC application

Ok. Let’s try to add some filter at the server side. Just remove the following codes from the Spring RESTFul controller.

                response.setHeader("Access-Control-Allow-Origin", "*");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		response.setHeader("Access-Control-Max-Age", "3600");
		response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

Then create the filter class by implementing javax.servlet.Filter interface. My filter class named SimpleCORSFilter will be look like below.

@Component
public class SimpleCORSFilter implements Filter {

	@Override
	public void init(FilterConfig arg0) throws ServletException {}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletResponse response=(HttpServletResponse) resp;

		response.setHeader("Access-Control-Allow-Origin", "*");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		response.setHeader("Access-Control-Max-Age", "3600");
		response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

		chain.doFilter(req, resp);
	}

	@Override
	public void destroy() {}

}

This filter doesn’t do anything right now. I have to configure the Spring application to aware of the filter in web.xml. Just put the following line at the end of web.xml file.

          <filter>
		<filter-name>simpleCORSFilter</filter-name>
		<filter-class>
			me.waiyan.demo.controller.SimpleCORSFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>simpleCORSFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

Now, Spring will put the headers specified in the SimpleCORSFilter to the response. By doing so, you don’t need to repeat the code in every controller.  As I said above, accepting all client origin is not practical and is a really security concern. Allowing only trusted client origin to consume your resources would be more resonable in the real world application.

Advertisements

6 thoughts on “CORS with AngularJS and Spring REST

  1. You saved me a lot of time.

    For information, because of recent chrome behavior
    you should change this line:
    response.setHeader(“Access-Control-Allow-Headers”, “x-requested-with”);
    by this:
    response.setHeader(“Access-Control-Allow-Headers”, “Origin, X-Requested-With, Content-Type, Accept”);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: