Servlet - Cross-origin resource sharing (CORS)

[Updated: Jun 19, 2017, Created: Apr 13, 2017]

In this tutorial, we will learn how to implement CORS in Java Servlet.

A page loaded from a domain (the origin) cannot make an Ajax call to other domain, unless the other domain sends back the response with some special Access-control-XYZ headers.

To demonstrate how it works, we are going to develop two web applications, both running on localhost but on two different ports.

The origin application

In this web application, we are going to use a static page, which will make an Ajax call to other application running on different port.

src/main/webapp/client.html

<!DOCTYPE html>
<html>
<head>
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
   <script>
    $(document).ready(function(){
        $("button").click(function(){
            $("#idDiv").load("http://localhost:8080/test");
        });
    });
   </script>
</head>
<body>
<h2>CORS CLIENT</h2>
 <div id="idDiv"></div>
<br/>
 <button>Make Ajax call</button>
</body>
</html>

We will deploy this application in the embedded tomcat container at port 9000, whereas the above Ajax call is made at port 8080.

pom.xml

<project .....>
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>servlet-cors-origin-app</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<port>9000</port>
</configuration>
</plugin>
</plugins>
</build>
</project>

The Servlet based application handing cross domain request

@WebServlet(name = "myServlet", urlPatterns = {"/test"})
public class MyServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
      setAccessControlHeaders(resp);
      PrintWriter writer = resp.getWriter();
      writer.write("test response from myServlet");
  }

  //for Preflight
  @Override
  protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
      setAccessControlHeaders(resp);
      resp.setStatus(HttpServletResponse.SC_OK);
  }

  private void setAccessControlHeaders(HttpServletResponse resp) {
      resp.setHeader("Access-Control-Allow-Origin", "http://localhost:9000");
      resp.setHeader("Access-Control-Allow-Methods", "GET");
  }
}

Note that we have added doOptions method as well, that's because the request other than GET/HEAD/POST having non standard content-type will be sent in two parts by the browser. The first part is called 'preflight', which will make an OPTIONS request. If the response of the first part approves the 'origin' domain, the browser will send the actual request. Check out the wiki diagram here.

We are going to deploy above servlet in tomcat container at the default port (8080)

pom.xml

<project .....>
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>servlet-cors-destination</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>

Running the applications

Run both applications one by one:

mvn tomcat7:run

Access the static page served by the first application:

Before making the Ajax call, let's check out the request/response of the Origin application in chrome developer tools:

Nothing's special here, other than that the browser remembers the Host url which will be used as 'origin' header when making request to other application.

Now click on the 'Make Ajax call' button.

CORS without Access-Control-Allow-Origin header

Now let's comment out setAccessControlHeaders call in doGet method:

     @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
      //  setAccessControlHeaders(resp);
        PrintWriter writer = resp.getWriter();
        writer.write("test response from myServlet");
    }

Restart the application and make the Ajax call again:

This time we didn't receive any Access-Control headers but still got '200 OK' status, that's because our Servlet is still returning the default status.

We don't see any response on our static html page. On chrome console we got this message:

Example Projects

The Origin application

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.3.9

Cors Origin App Select All Download
  • servlet-cors-origin-app
    • src
      • main
        • java
          • com
            • logicbig
        • webapp

The CORS handler application

Dependencies and Technologies Used :

  • javax.servlet-api 3.1.0 Java Servlet API
  • JDK 1.8
  • Maven 3.3.9

Cross Servlet App Select All Download
  • servlet-cors-destination
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also