解決ajax跨域問題【5種解決方案】javascript
什麼是跨域問題?
跨域問題來源於JavaScript的"同源策略",即只有 協議+主機名+端口號 (如存在)相同,則容許相互訪問。也就是說JavaScript只能訪問和操做本身域下的資源,不能訪問和操做其餘域下的資源。跨域問題是針對JS和ajax的,html自己沒有跨域問題。html
查看瀏覽器開發者工具Console報錯:java
Failed to load http://a.a.com:8080/A/FromServlet?userName=123: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://b.b.com:8080' is therefore not allowed access.jquery
http://www.abc.com/a/b 調用 http://www.abc.com/d/c(非跨域)nginx
http://www.abc.com/a/b 調用 http://www.def.com/a/b (跨域:域名不一致)ajax
http://www.abc.com:8080/a/b 調用 http://www.abc.com:8081/d/c (跨域:端口不一致)spring
http://www.abc.com/a/b 調用 https://www.abc.com/d/c (跨域:協議不一樣)json
請注意:localhost和127.0.0.1雖然都指向本機,但也屬於跨域。後端
跨域問題怎麼解決?
一、響應頭添加Header容許訪問跨域
二、jsonp 只支持get請求不支持post請求
三、httpClient內部轉發
四、使用接口網關——nginx、springcloud zuul (互聯網公司常規解決方案)
解決方式1:響應頭添加Header容許訪問
跨域資源共享(CORS)Cross-Origin Resource Sharing
這個跨域訪問的解決方案的安全基礎是基於"JavaScript沒法控制該HTTP頭"
它須要經過目標域返回的HTTP頭來受權是否容許跨域訪問。
response.addHeader(‘Access-Control-Allow-Origin:*’);//容許全部來源訪問
response.addHeader(‘Access-Control-Allow-Method:POST,GET’);//容許訪問的方式
解決方式2:jsonp 只支持get請求不支持post請求
用法:①dataType改成jsonp ②jsonp : "jsonpCallback"————發送到後端實際爲http://a.a.com/a/FromServlet?userName=644064&jsonpCallback=jQueryxxx ③後端獲取get請求中的jsonpCallback ④構造回調結構
$.ajax({
type : "GET",
async : false,
url : "http://a.a.com/a/FromServlet?userName=644064",
dataType : "jsonp",//數據類型爲jsonp
jsonp : "jsonpCallback",//服務端用於接收callback調用的function名的參數
success : function(data) {
alert(data["userName"]);
},
error : function() {
alert('fail');
}
});
//後端
String jsonpCallback = request.getParameter("jsonpCallback");
//構造回調函數格式jsonpCallback(數據)
resp.getWriter().println(jsonpCallback+"("+jsonObject.toJSONString()+")");
JSONP實現原理
在同源策略下,在某個服務器下的頁面是沒法獲取到該服務器之外的數據的,即通常的ajax是不能進行跨域請求的。但 img、iframe 、script等標籤是個例外,這些標籤能夠經過src屬性請求到其餘服務器上的數據。利用<script>標籤的開放策略,咱們能夠實現跨域請求數據,固然這須要服務器端的配合。 Jquery中ajax的核心是經過 XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標籤來調用服務器提供的 js腳本。
當咱們正常地請求一個JSON數據的時候,服務端返回的是一串JSON類型的數據,而咱們使用 JSONP模式來請求數據的時候服務端返回的是一段可執行的JavaScript代碼。由於jsonp 跨域的原理就是用的動態加載<script>的src ,因此咱們只能把參數經過url的方式傳遞,因此jsonp的 type類型只能是get !
示例:
$.ajax({
url: 'http://192.168.10.46/demo/test.jsp', //不一樣的域
type: 'GET', // jsonp模式只有GET 是合法的
data: {
'action': 'aaron'
},
dataType: 'jsonp', // 數據類型
jsonp: 'jsonpCallback', // 指定回調函數名,與服務器端接收的一致,並回傳回來
})
其實jquery 內部會轉化成
http://192.168.10.46/demo/test.jsp?jsonpCallback=jQuery202003573935762227615_1402643146875&action=aaron
而後動態加載
<script type="text/javascript"src="http://192.168.10.46/demo/test.jsp?jsonpCallback= jQuery202003573935762227615_1402643146875&action=aaron"></script>
而後後端就會執行jsonpCallback(傳遞參數 ),把數據經過實參的形式發送出去。
使用JSONP 模式來請求數據的整個流程:客戶端發送一個請求,規定一個可執行的函數名(這裏就是 jQuery作了封裝的處理,自動幫你生成回調函數並把數據取出來供success屬性方法來調用,而不是傳遞的一個回調句柄),服務器端接受了這個 jsonpCallback函數名,而後把數據經過實參的形式發送出去
(在jquery 源碼中, jsonp的實現方式是動態添加<script>標籤來調用服務器提供的 js腳本。jquery 會在window對象中加載一個全局的函數,當 <script>代碼插入時函數執行,執行完畢後就 <script>會被移除。同時jquery還對非跨域的請求進行了優化,若是這個請求是在同一個域名下那麼他就會像正常的 Ajax請求同樣工做。)
解決方式3:httpClient內部轉發
實現原理很簡單,若想在B站點中經過Ajax訪問A站點獲取結果,當然有ajax跨域問題,但在B站點中訪問B站點獲取結果,不存在跨域問題,這種方式其實是在B站點中ajax請求訪問B站點的HttpClient,再經過HttpClient轉發請求獲取A站點的數據結果。但這種方式產生了兩次請求,效率低,但內部請求,抓包工具沒法分析,安全。
$.ajax({
type : "GET",
async : false,
url : "http://b.b.com:8080/B/FromAjaxservlet?userName=644064",
dataType : "json",
success : function(data) {
alert(data["userName"]);
},
error : function() {
alert('fail');
}
});
@WebServlet("/FromAjaxservlet")
public class FromAjaxservlet extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//建立默認鏈接
CloseableHttpClient httpClient = HttpClients.createDefault();
//建立HttpGet對象,處理get請求,轉發到A站點
HttpGet httpGet = new HttpGet("http://a.a.com:8080/A/FromServlet?userName="+req.getParameter("userName"));
//執行
CloseableHttpResponse response = httpClient.execute(httpGet);
int code = response.getStatusLine().getStatusCode();
//獲取狀態
System.out.println("http請求結果爲:"+code);
if(code == 200){
//獲取A站點返回的結果
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
//把結果返回給B站點
resp.getWriter().print(result);
}
response.close();
httpClient.close();
} catch (Exception e) {
}
}
}
解決方式4:使用nginx搭建企業級接口網關方式
www.a.a.com不能直接請求www.b.b.com的內容,能夠經過nginx,根據同域名,但項目名不一樣進行區分。什麼意思呢?這麼說可能有點抽象。假設咱們公司域名叫www.nginxtest.com
當咱們須要訪問www.a.a.com經過www.nginxtest.com/A訪問,並經過nginx轉發到www.a.a.com
當咱們須要訪問www.b.b.com經過www.nginxtest.com/B訪問,並經過nginx轉發到www.a.a.com
咱們訪問公司的域名時,是"同源"的,只是項目名不一樣,此時項目名的做用只是爲了區分,方便轉發。若是你還不理解的話,先看看我是怎麼進行配置的:
server {
listen 80;
server_name www.nginxtest.com;
location /A {
proxy_pass http://a.a.com:81;
index index.html index.htm;
}
location /B {
proxy_pass http://b.b.com:81;
index index.html index.htm;
}
}
咱們訪問以www.nginxtest.com開頭且端口爲80的網址,nginx將會進行攔截匹配,若項目名爲A,則分發到a.a.com:81。實際上就是經過"同源"的域名,不一樣的項目名進行區分,經過nginx攔截匹配,轉發到對應的網址。整個過程,兩次請求,第一次請求nginx服務器,第二次nginx服務器經過攔截匹配分發到對應的網址。
解決方式5:使用Spring Cloud zuul接口網關————————————————版權聲明:本文爲CSDN博主「itcats_cn」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/itcats_cn/article/details/82318092