本來想說是深入研究一下,但是水準有限,也就是手放在水面往下多0.25厘米的深度的樣子
,掠于皮毛。
文章目錄
- 什麼是跨域問題
- 跨域問題模拟
- 跨域問題如何解決
-
- response添加請求頭解決跨域問題
- jsonp解決跨域問題
- httpclient内部轉發解決跨域問題
- 寫在後面
什麼是跨域問題
跨域其實是浏覽器的一個安全機制,請求通路域名與AJAX請求位址不一緻,浏覽器會無法傳回請求結果。
具體的例子,現在我在浏覽器通路url位址www.aaa.com/a,而這個頁面背景伺服器又發送請求給www.bbbb.com/b,因為浏覽器跨域安全機制,阻止你的通路。
跨域問題模拟
我們先配置一下host,添加兩個自定義的域名便于我們測試
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL1ETOwMDOyEjM1ITMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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伺服器去轉發。