Cross-domain issues in Ajax requests and their solutions

Cross-domain issues

Cross-domain refers to requesting the resources of another domain name from a web page of one domain name, such as currently requesting the JD server (https://www.jd.com) on the Baidu page (https://baidu.com) Resources

Traditional requests will not cross domains

On the a site, you can submit it through hyperlink or form form or window.location.href Cross-domain access to b site resources (static or dynamic)

  • The essence of the traditional method is to send a request on the address bar to directly jump to the page, instead of obtaining the resources of another page from the current page.
<!--Sending requests through hyperlinks can cross domains-->
<a href="http://localhost:8081/b/index.html">Cross-domain access to the index page of site b</a>
<br>
<!--Requests sent through forms can be cross-domain-->
<form action="http://localhost:8081/b/user/reg" method="post">
    Username: <input type="text" name="username"><br>
    Password: <input type="password" name="password"><br>
    <input type="submit" value="Register">
</form>
<br>
<!--Sending requests through window.location.href/document.location.href in js code can cross domains-->
<button onclick="window.location.href='http://localhost:8081/b/index.html'">Cross-domain access to the index page of site b</button>
<button onclick="document.location.href='http://localhost:8081/b/index.html'">Cross-domain access to the index page of site b</button>
<!--Use the src attribute of the script tag to load js files across domains -->
<script type="text/javascript" src="http://localhost:8081/b/my.js"></script>
<br>
<!--The src attribute of img can also load images from other sites across domains-->
<img src="http://localhost:8081/b/bd_logo.png" />

b site’s static resource index.html page

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Bapplication index page</title>
    </head>
    <body>
        <h1>Bapplication index page</h1>
    </body>
</html>

Dynamic resource UserRegServlet of b site

@WebServlet("/user/reg")
public class UserRegServlet extends HttpServlet {<!-- -->
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!-- -->
        // Get username and password
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //Respond to the front end
        response.getWriter().print(username + ":" + password);
    }
}

Ajax request cross-domain issue

Send an Ajax request on the page of site a to access the resources of site b. The request initiated at this time is a cross-domain request

<script type="text/javascript">
    // Use ES6 new features: arrow functions
    window.onload = () => {<!-- -->
        document.getElementById("btn").onclick = () => {<!-- -->
            // 1. Create core objects (new features of ES6: variables can be defined using the var let const keyword)
            let xmlHttpRequest = new XMLHttpRequest();
            // 2. Register callback function
            xmlHttpRequest.onreadystatechange = () => {<!-- -->
                if (xmlHttpRequest.readyState == 4) {<!-- -->
                    // Status codes are acceptable within this range
                    if (xmlHttpRequest.status >= 200 & amp; & amp; xmlHttpRequest.status < 300) {<!-- -->
                        document.getElementById("mydiv").innerHTML = xmlHttpRequest.responseText
                    }
                }
            }
            // 3. Open the channel
            xmlHttpRequest.open("GET", "http://localhost:8081/b/hello", true)
            // 4. Send request
            xmlHttpRequest.send()
        }
    }
</script>
<button id="btn">Send ajax cross-domain request</button>
<div id="mydiv"></div>
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {<!-- -->
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!-- -->
        // response
        response.getWriter().print("hello ajax!!!");
    }
}

Due to the browser’s same-origin policy, the browser will intercept the server’s response data, causing the page to be unable to render the server’s response data

# chrome browser error
Access to XMLHttpRequest at 'http://localhost:63110/system/dictionary/all' from origin 'http://localhost:8601' has been blocked by CORS policy: No 'Access-Control-Allow- Origin' header is present on the requested resource

#firefox browser error
Cross-origin request intercepted: The same origin policy prohibits reading the remote resource located at http://localhost:63110/system/dictionary/all (reason: CORS header missing 'Access-Control-Allow-Origin')

AJAX cross-domain solution

Sometimes we need to use ajax for cross-domain access. For example, a company’s page A (a.bjpowernode.com) needs to obtain the resources of page B (b.bjpowernode.com)

Original policy (CORS policy)

The same origin policy means that a script can only read the properties of windows and documents from the same source, which is beneficial to protecting website information)

  • Same origin means that the protocol, domain name, and port are all the same. Even if two different domain names point to the same IP address, they are not from the same origin.
  • If there is no same-origin policy, when you enter your account and password on the online banking site and then visit some irregular websites, these websites can access the online banking site just now to obtain your account and password.

When requesting from one address to another, if the protocol, domain name, and port number are all consistent, it means the same source. If there is an inconsistency, it is a cross-domain request. This The browser will add origin to the request header.

  • The same-origin policy is a security mechanism of browsers. Based on the same-origin policy, the browser determines whether the request initiated by the user is a cross-domain request to prevent the browser from being attacked by XSS, CSRF, etc.
  • Cross-domain requests only occur between the browser and the server. There are no cross-domain requests between servers.
  • Cross-domain does not mean that the request cannot be sent. The request can be sent and the server can receive the request and return the result normally. It is just that the response data is intercepted by the browser because the browser thinks that you initiated it. It is a cross-domain request
  • Browsers are not allowed to share the same XMLHttpRequest object across domains because it is unsafe to share the same XMLHttpRequest object.
URL1 URL2 Whether it has the same origin Description
http://localhost:8080/a/index.html http://localhost:8080/a/first Same origin The protocol domain name and port are consistent
http://localhost:8080/a/index.html http://localhost :8080/b/first Same source The protocol domain name and port are the same
http://www.myweb.com: 8080/a.js https://www.myweb.com:8080/b.js Different sources Different protocols
http://www.myweb.com:8080/a.js http://www.myweb.com:8081/b.js Different sources Different ports
http://www.myweb.com/a.js http: //www.myweb2.com/b.js Different sources Different domain names
http://www.myweb .com/a.js http://crm.myweb.com/b.js Different sources Different subdomain names

CORS (response header) solution

CORS (Cross-origin resource sharing) is cross-domain resource sharing, which allows the browser to issue XMLHttpRequest requests to cross-origin servers, thereby overcoming the limitation that AJAX can only be used from the same origin

When the server responds to the browser, it adds the response header Access-Control-Allow-Origin, which tells the browser which sites are allowed to access resources across domains, so that the browser will allow the corresponding sites.

response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // Allow a site
response.setHeader("Access-Control-Allow-Origin", "*"); // Allow all sites

jsonp solution

jsonp (json with padding) means that it is json with padding. Similar to ajax requests, it can complete the partial refresh effect and solve cross-domain problems. But it only supports GET requests, not Support POST request

Make a cross-domain request through the src attribute of the script tag, and the JS code responded by the server will be parsed within the script tag

  • The src attribute of the script tag can cross-domain access static xxx.js resource file, and can also cross-domain request a dynamic Java program. The result of the request response will be in the script tag. internal parsing
<script type="text/javascript">
    //Custom function
    function sayHello(data){<!-- -->
        alert("hello," + data.name)
    }
</script>
<!---->
<script type="text/javascript" src="http://localhost:8081/b/jsonp?fun=sayHello">
//The JS code responded by the server will be parsed within the script tag
    sayHello({<!-- -->"name" : "jackson"})
</script>

Step 1: Don’t create the script tag when the page is opened. When we click the button, create the script element and set the src attribute code>Send jsonp request to achieve partial page refresh effect

<!--<script type="text/javascript" src="http://localhost:8081/b/jsonp?fun=sayHello"></script>-->
<script type="text/javascript">
  // Custom function, parameter data is a json object
  function sayHello(data){<!-- -->
    document.getElementById("mydiv").innerHTML = data.username
  }
  window.onload = () => {<!-- -->
    document.getElementById("btn").onclick = () => {<!-- -->
      // Create script element object
      const htmlScriptElement = document.createElement("script");
      //Set the type attribute of script
      htmlScriptElement.type = "text/javascript"
      //Set the src attribute of the script
      htmlScriptElement.src = "http://localhost:8081/b/jsonp?fun=sayHello"
      //Add the script object to the body tag (this step is to load the script)
      document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)
    }
  }
</script>
<button id="btn">jsonp solves cross-domain problems and achieves the effect of ajax partial refresh</button>
<div id="mydiv"></div>

Step 2: The backend processes the jsonp request initiated by the frontend and responds to a piece of JS code. The backend response is always a string, but the browser will automatically treat this string as a piece of code. JS code is parsed and executed within the script tag

@WebServlet("/jsonp")
public class JSONPSerlet1 extends HttpServlet {<!-- -->
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!-- -->
        //Respond to a js code to the front end
        PrintWriter out = response.getWriter();
        //In response to a JS built-in alert function, the browser can directly call it after parsing it.
        //out.print("alert(123)");
        //Directly respond to a programmer-defined sayHello function, the parameter is a json object
        //out.print("sayHello({"name" : "jackson"})");
        //Dynamicly obtain the function name corresponding to the request parameter in the URL. The method parameter is a json object.
        String fun = request.getParameter("fun");
        out.print(fun + "({"name" : "jackson"})");
    }
}

jQuery encapsulated jsonp

Use jsonp encapsulated by jQuery directly, the underlying principle remains unchanged

<!--The official jQuery library introduced is from the official website-->
<script type="text/javascript" src="/ajax/js/jquery-3.6.0.min.js"></script>

<script type="text/javascript">
    // You don’t need to write this function, jQuery can help you automatically generate a callback
    function jQuery3600508253314856699_1655528968612(json){<!-- -->
   // Will continue to automatically call the success callback function
    }

    //custom function
    function sayHello(data){<!-- -->
        $("#mydiv").html("Welcome:" + data.username)
    }

    $(function(){<!-- -->
        $("#btn").click(function(){<!-- -->
            //Send an ajax-like request, which is not essentially an ajax request.
            $.ajax({<!-- -->
                type: "GET", // jsonp request only supports get request
        //The actual request sent is /b/jsonp?callback=jQuery3600508253314856699_1655528968612 &_=1655528968613 (timestamp)
                // callback is the request parameter name, jQuery3600508253314856699_1655528968612 is the random function name
                url: "http://localhost:8081/b/jsonp",//Specify the cross-domain url
                dataType: "jsonp", // The specified data type is in jsonp format
                jsonp: "fun", //Specify the specific request parameter name (previously fun), the default request parameter name is callback
                jsonpCallback: "sayHello" // Specify a custom callback function name (previously sayHello). By default, jQuery will automatically generate a random callback function.
                
                // The callback function automatically generated by jQuery will automatically call the success callback function.
                /*success: function(data){ // data is a json object used to receive server-side response data
                    $("#mydiv").html("Welcome:" + data.username)
                }*/
            })
        })
    })
</script>
<button id="btn">jsonp encapsulated by jQuery library</button>
<div id="mydiv"></div>

Principle of agency mechanism

There will be no cross-domain problems when the pages of this site access the ProxyServlet of the current site, and then send GET/POST requests through ProxyServlet to access cross-domain resources

  • The first solution (complicated code): Use JDK built-in API (java.net.URL) to send HTTP requests
  • The second option (recommended): Use a third-party open source component to send http requests, such as apache’s open source and free httpclient component

Step one: Access the ProxyServlet in the current site from the page of a site

<script type="text/javascript">
    // There is a new syntax arrow function in ES6
    window.onload = () => {<!-- -->
        document.getElementById("btn").onclick = () => {<!-- -->
            // 1. Create core object
            const xmlHttpRequest = new XMLHttpRequest();
            // 2. Register callback function
            xmlHttpRequest.onreadystatechange = () => {<!-- -->
                if (xmlHttpRequest.readyState == 4) {<!-- -->
                    // You can also use the interval method here, because the status code is 200~299, which is the end of the normal response.
                    if (xmlHttpRequest.status >= 200 & amp; & amp; xmlHttpRequest.status < 300) {<!-- -->
                        document.getElementById("mydiv").innerHTML = xmlHttpRequest.responseText
                    }
                }
            }
            // 3. Open the channel
            xmlHttpRequest.open("GET", "/a/proxy", true)
            // 4.Send request
            xmlHttpRequest.send()
        }
    }
</script>
<button id="btn">Use proxy mechanism to solve ajax cross-domain access</button>
<div id="mydiv"></div>

Step 2: Send a GET request through the httpclient component in ProxyServlet of site a to access the TargetServlet of site b

  • The TargetServlet of b site will respond the data to the ProxyServlet of a site, and then the ProxyServlet will respond the data to the front end.
@WebServlet("/proxy")
public class ProxyServlet extends HttpServlet {<!-- -->
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!-- -->
        // target address
        HttpGet httpGet = new HttpGet("http://localhost:8081/b/target");
        // Set type "application/x-www-form-urlencoded" "application/json"
        httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
        //System.out.println("Calling URL: " + httpGet.getURI());
        
        // httpClient instantiation
        CloseableHttpClient httpClient = HttpClients.createDefault();
//Initiate a request to access the TargetServlet of site b
        HttpResponse resp = httpClient.execute(httpGet);
        // Get the data that TargetServlet responds to ProxyServlet
        HttpEntity entity = resp.getEntity();
        System.out.println("Return status code:" + response.getStatusLine());
        // Display the data responded by TargetServlet to ProxyServlet
        BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
        String line = null;
        StringBuffer responseSB = new StringBuffer();
        while ((line = reader.readLine()) != null) {<!-- -->
            responseSB.append(line);
        }
        //System.out.println("Server response data:" + responseSB);
        reader.close();
        httpClient.close();
        // Continue to respond to the data returned by the TargetServlet of site b to the front end.
        response.getWriter().print(responseSB);
    }
}

Step 3: Site b’s TargetServlet handles the ajax request initiated by site a’s ProxyServlet

@WebServlet("/target")
public class TargetServlet extends HttpServlet {<!-- -->
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!-- -->
        //Response to a json string
        response.getWriter().print("{"username":"jackson"}");
    }
}

Nginx reverse proxy

The implementation principle of the proxy server solution: The same-origin policy is a standard that browsers need to follow, but if the server makes a request to the server, it does not need to follow the same-origin policy

Nginx’s reverse proxy also uses a proxy mechanism similar to ProxyServlet to complete Ajax cross-domain

  • Step 1: The browser first accesses the front-end address page provided by Nginx http://192.168.101.10:8601
  • Step 2: The request initiated by the front-end page will first access a same-origin address provided by Nginx (similar to ProxyServlet) http://192.168.101.11:8601/api/Specific request initiated by the front-end
  • Step 3: Since there is no cross-domain between servers, cross-domain access to the target address http://www.baidu.com:8601 can be achieved through ProxyServlet

Other common solutions

Common agency solutions

  • Node middleware proxy, postMesssage, websocket, window.name + iframe, location.hash + iframe, document.domain + iframe, etc.
  • vue-cli (the 8080 server that comes with Vue scaffolding can also be used as a proxy server, but you need to configure vue.config.js to enable this proxy. For details, please refer to the article
syntaxbug.com © 2021 All Rights Reserved.