天天看點

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

本來想說是深入研究一下,但是水準有限,也就是手放在水面往下多0.25厘米的深度的樣子

,掠于皮毛。

文章目錄

  • 什麼是跨域問題
  • 跨域問題模拟
  • 跨域問題如何解決
    • response添加請求頭解決跨域問題
    • jsonp解決跨域問題
    • httpclient内部轉發解決跨域問題
  • 寫在後面

什麼是跨域問題

跨域其實是浏覽器的一個安全機制,請求通路域名與AJAX請求位址不一緻,浏覽器會無法傳回請求結果。

具體的例子,現在我在浏覽器通路url位址www.aaa.com/a,而這個頁面背景伺服器又發送請求給www.bbbb.com/b,因為浏覽器跨域安全機制,阻止你的通路。

跨域問題模拟

我們先配置一下host,添加兩個自定義的域名便于我們測試

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

a web項目建立一個測試servlet,把tomcat啟動起來

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("name");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);
        resp.getWriter().write(jsonObject.toJSONString());
    }
}
           

b web項目的index.jsp通路a的測試接口

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>

  <script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>

  <script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "GET",
        async : false,
        url : "http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123",
        dataType : "json",
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>
  <body>
  </body>
</html>
           

通路b web項目的index.jsp,就會報跨域錯誤

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面
閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

這是因為AJAX請求的域名和我目前通路的域名是不一緻的,浏覽器預設不會擷取ajax結果。

浏覽器預設網站ajax請求是在同一個域名下的。這是浏覽器的機制。

跨域問題如何解決

使用jsonP 隻支援get請求,不支援post

使用接口網關 ,springcloud (企業開發常用)

httpclient 内部轉發

response添加header請求頭允許跨域。

但是我水準有限,springcloud先放一放

先說一下幾種方法

response添加請求頭解決跨域問題

在a web項目中加多一段代碼

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);
        resp.getWriter().write(jsonObject.toJSONString());
        // 允許浏覽器跨域通路,*允許所有
        resp.setHeader("Access-Control-Allow-Origin", "*");
    }
}
           

b網站重新通路,正常傳回結果

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

jsonp解決跨域問題

JSONP使用在前端中的

JSON隻支援GET,也不怎麼好用

修改ajax代碼,dataType改為jsonp,加一行jsonp: “jsonpCallback”

<script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "GET",
        async : false,
        url : "http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123",
        dataType : "jsonp",
        jsonp: "jsonpCallback",// 伺服器端用于接收回調的func參數
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>
           

伺服器端修改,擷取jsonpCallback參數,構造String傳回結果,

jsonpCallback + ( + 傳回内容 + )

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);

        String jsonpCallback = req.getParameter("jsonpCallback");
        System.out.println(jsonpCallback);

        resp.getWriter().println(
                String
                .format("%s(%s)",
                        jsonpCallback,
                        jsonObject.toJSONString()));

        //resp.setHeader("Access-Control-Allow-Origin", "*");
    }
}
           

檢視結果,通路url後面帶有jsonpCallback參數,這個參數是用戶端生成出來的,通路的時候jsonpCallback的值幫我們自動構造了。對應jsonp: “jsonpCallback”

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

響應結果

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面
閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

原理是什麼呢?

使用script發送get請求,我們在引用jquery.min.js的時候用的就是script請求,它是能跨越擷取結果的,jsonp用這個這個方法發送請求,回調參數,将參數解析,擷取取結果。

這裡參數回調結果是

jsonpCallback=jQuery17206283321772985047_1611579842743&_=1611579842749

解析參數,就取得了伺服器發送的結果

&_=1611579842749 -> username: “123”

注意類型是script類型的

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

jsonp是不支援post的,我們可以測試一下

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("調用了GET");
        //String username = req.getParameter("username");
        //JSONObject jsonObject = new JSONObject();
        //jsonObject.put("username", username);
        //
        //String jsonpCallback = req.getParameter("jsonpCallback");
        //System.out.println(jsonpCallback);
        //
        //resp.getWriter().write(
        //        String
        //        .format("%s(%s)",
        //                jsonpCallback,
        //                jsonObject.toJSONString()));

        //resp.setHeader("Access-Control-Allow-Origin", "*");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("調用了POST");
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);

        String jsonpCallback = req.getParameter("jsonpCallback");
        System.out.println(jsonpCallback);

        resp.getWriter().write(
                String
                        .format("%s(%s)",
                                jsonpCallback,
                                jsonObject.toJSONString()));
    }
}
           

把前端代碼改成POST請求,通路測試,alert了fail,但是傳回200,請求方式自動變成了GET

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

代碼明明請求方式寫的是post的

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

伺服器背景列印,完全沒有走post的方法

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

是以jsonp不支援post

httpclient内部轉發解決跨域問題

b項目内部發送httpclient給a項目

b項目引入httpclient jar包

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
           

寫httpclient轉發

@WebServlet("/ForwardToAServerServlet")
public class ForwardToAServerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 建立預設連接配接
        CloseableHttpClient httpClient = HttpClients.createDefault();

        // 建立get連接配接
        HttpGet httpGet = new HttpGet("http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123");
        // 執行通路
        CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpGet);

        // 擷取狀态碼
        int code = closeableHttpResponse.getStatusLine().getStatusCode();

        // 擷取狀态
        System.out.println("code:" + code);

        if (code == 200) {
            resp.getWriter().write(
                    EntityUtils
                            .toString(closeableHttpResponse.getEntity()));
        }

        // 關閉連接配接
        closeableHttpResponse.close();
        httpClient.close();
    }
}

           

通路ForwardToAServerServlet接口,是經由httpclient調用接口擷取的結果

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

那修改index.jsp,通路自己的ForwardToAServerServlet接口就行了,成功的繞過了浏覽器的跨域檢測

<html>
  <head>
    <title>$Title$</title>
  </head>

  <script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>

  <script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "get",
        async : false,
        url : "http://b.b.com:8081/javaWEBquick_war_exploded//ForwardToAServerServlet",
        dataType : "json",
        //jsonp: "jsonpCallback",// 伺服器端用于接收回調的func參數
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>
  <body>
  </body>
</html>

           

成功解決跨域問題

閉關修煉(二十四)淺入了解跨域問題什麼是跨域問題跨域問題模拟跨域問題如何解決寫在後面

使用httpclient的缺點是效率點,有點類似重定向,做了兩次請求。

這種方法最大好處是十分安全,抓包無法分析你真正調用的接口,通路接口的請求隐藏在了httpclient轉發中,類似加了層殼。

寫在後面

其實在項目中都不用這些辦法解決跨域問題。

小demo中快速解決跨域,可以用用。

項目一般使用網關接口,搭建api接口網關,那麼原理是什麼呢?

伺服器搭建nginx,伺服器部署了多個不同的項目,用戶端通路nginx伺服器,nginx判斷項目名稱轉發用戶端的通路。

這樣做的好處是:用戶端都通路一個域名,而跨域的工作交給nginx伺服器去轉發。