如何解決跨域問題?首先我們需要知道什么是跨域,跨域指的是瀏覽器不能執(zhí)行其它網(wǎng)站的腳本,它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript 施加的安全限制。
1、同源策略
根據(jù)百度百科 同源策略它是由 Netscape 提出的一個安全策略,它是瀏覽器最核心也是最基本的安全功能,如果缺少同源策略,則瀏覽器的正常功能可能都會受到影響,現(xiàn)在所有支持JavaScript的瀏覽器都會使用這個策略。
所謂同源指的是:
協(xié)議、域名、端口號都相同,只要有一個不相同,那么都是非同源。
瀏覽器在執(zhí)行腳本的時候,都會檢查這個腳本屬于哪個頁面,即檢查是否同源,只有同源的腳本才會被執(zhí)行;而非同源的腳本在請求數(shù)據(jù)的時候,瀏覽器會報一個異常,提示拒絕訪問。
①、http://www.123.com/index.html 調(diào)用 http://www.123.com/welcome.jsp 協(xié)議、域名、端口號都相同,同源。
②、https://www.123.com/index.html 調(diào)用 http://www.123.com/welcome.jsp 協(xié)議不同,非同源。
③、http://www.123.com:8080/index.html 調(diào)用 http://www.123.com:8081/welcome.jsp 端口不同,非同源。
④、http://www.123.com/index.html 調(diào)用 http://www.456.com/welcome.jsp 域名不同,非同源。
⑤、http://localhost:8080/index.html 調(diào)用 http://127.0.0.1:8080/welcom.jsp 雖然localhost等同于 127.0.0.1 但是也是非同源的。
同源策略限制的情況:
1、Cookie、LocalStorage 和 IndexDB 無法讀取
2、DOM 和 Js對象無法獲得
3、AJAX 請求不能發(fā)送
注意:對于像 img、iframe、script 等標(biāo)簽的 src 屬性是特例,它們是可以訪問非同源網(wǎng)站的資源的。
2、跨域?qū)嵗菔?/h3>
我們創(chuàng)建了兩個 web 項目JavaWeb01 和 JavaWeb02 分別部署在tomcat1和Tomcat2上,這兩個 Tomcat 的端口號設(shè)置是不一樣的,一個是 8080,一個是8081,所以這兩個項目構(gòu)成了非同源。那么我們從客戶端(瀏覽器)輸入訪問部署在 Tomcat2 上的項目 JavaWeb2,然后在該項目中通過 ajax 去請求部署在 Tomcat1 上的項目數(shù)據(jù),能夠訪問的到呢?
①、在 JavaWeb02 項目中,有一個 jsp 文件,我們通過在瀏覽器訪問該 JSP 文件去獲取 JavaWeb01 項目中的數(shù)據(jù)
< %@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"% >
< %
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path;
% >
< !DOCTYPE html >
< head >
< title >Title< /title >
< /head >
< script type="text/javascript" src="< %=basePath% >/js/jquery-3.3.1.min.js" >< /script >
< script type="text/javascript" >
$(document).ready(function(){
$.ajax({
type:"get",
async:false,
url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom",
dataType:"json",
success:function (data) {
alert(data['passWord']);
},
error:function () {
alert("error");
}
});
})
< /script >
< body >
< /body >
< /html >
通過ajax 訪問
url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom"
去獲取 JavaWeb01 項目中的數(shù)據(jù)。
②、在 JavaWeb01 項目中,創(chuàng)建一個 getPassWordByUserNameServlet 請求的 Servlet
package com.ys.servlet;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Create by YSOcean
*/
@WebServlet("/getPassWordByUserNameServlet")
public class UserServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("userName");
String passWord = null;
if(userName != null){
passWord = "123";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("passWord",passWord);
resp.getWriter().println(jsonObject.toJSONString());
}
}
③、在瀏覽器中輸入 http://localhost:8081/JavaWeb02/index.jsp 鏈接,去調(diào)用該頁面的 ajax 函數(shù)
瀏覽器給我們返回了一個錯誤,這就是瀏覽器同源策略導(dǎo)致的跨域訪問會報錯。那么該如何解決呢?
3、跨域解決辦法
①、response 添加 header 我們在 Servlet 請求返回時添加如下代碼:
//*表示支持所有網(wǎng)站訪問,也可以額外配置相應(yīng)網(wǎng)站
resp.setHeader("Access-Control-Allow-Origin", "*");
請求結(jié)果如下:
②、JSONP 方式 首先我們要修改 index.jsp 頁面的 ajax 請求:
$.ajax({
type:"get",
async:false,
url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom",
dataType:"jsonp",//數(shù)據(jù)類型為jsonp
jsonp:"backFunction",//服務(wù)端用于接收callBack調(diào)用的function名的參數(shù)
success:function (data) {
alert(data["passWord"]);
},
error:function () {
alert("error");
}
});
注意:我們修改了 dataType 的數(shù)據(jù)類型為 jsonp,并且新增了 jsop 屬性值為 “backFunction”。
接著在 JavaWeb01 項目的 Servlet 中進行如下修改:
@WebServlet("/getPassWordByUserNameServlet")
public class UserServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("userName");
String passWord = null;
if(userName != null){
passWord = "123";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("passWord",passWord);
//1、第一種方法:*表示支持所有網(wǎng)站訪問,也可以額外配置相應(yīng)網(wǎng)站
//resp.setHeader("Access-Control-Allow-Origin", "*");
//2、第二種方法:jsonp
String backFunction = req.getParameter("backFunction");
resp.getWriter().println(backFunction+"("+jsonObject.toJSONString()+")");
//resp.getWriter().println(jsonObject.toJSONString());
}
}
結(jié)果就不截圖了,下面講講這種方式的原理。
1、在同源策略下,在某個服務(wù)器下的頁面是無法獲取到該服務(wù)器以外的數(shù)據(jù)的,即一般的ajax是不能進行跨域請求的。但 img、iframe 、script等標(biāo)簽是個例外,這些標(biāo)簽可以通過src屬性請求到其他服務(wù)器上的數(shù)據(jù)。利用 script標(biāo)簽的開放策略,我們可以實現(xiàn)跨域請求數(shù)據(jù),當(dāng)然這需要服務(wù)器端的配合。Jquery中ajax 的核心是通過 XmlHttpRequest獲取非本頁內(nèi)容,而jsonp的核心則是動態(tài)添加 標(biāo)簽來調(diào)用服務(wù)器提供的 js腳本。
2、當(dāng)我們正常地請求一個JSON數(shù)據(jù)的時候,服務(wù)端返回的是一串 JSON類型的數(shù)據(jù),而我們使用 JSONP模式來請求數(shù)據(jù)的時候服務(wù)端返回的是一段可執(zhí)行的 JavaScript代碼。因為jsonp 跨域的原理就是用的動態(tài)加載 script的src ,所以我們只能把參數(shù)通過 url的方式傳遞,所以jsonp的 type類型只能是get !
我們可以看上面的請求,瀏覽器按 F12 顯示如下:
我們將這段路徑單獨復(fù)制出來:
http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom&backFunction=jQuery33107285685756141047_1532791502227&_=1532791502228
再看 Preview 頁:
也就是說對于上面的JSONP 請求,其實jQuery會轉(zhuǎn)化為:
< script type="text/javascript"
src="http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom&backFunction=jQuery33107285685756141047_1532791502227&_=1532791502228" >
< /script >
然后動態(tài)的去加載該 script 標(biāo)簽的 src 屬性。
③、HttpClient 請求轉(zhuǎn)發(fā) 這種方式客戶端是向 JavaWeb02 項目發(fā)送請求,而不是上面的向 JavaWeb01 發(fā)送請求,然后在 JavaWeb02 的后臺通過 HttpClient 將請求發(fā)送到 JavaWeb01,得到數(shù)據(jù)后返回。這種方式相當(dāng)于繞過瀏覽器的同源機制,直接通過后端進行轉(zhuǎn)發(fā)。
index.jsp 的ajax請求如下:
$.ajax({
type:"get",
async:false,
url:"http://localhost:8081/JavaWeb02/ToGetPassWordServlet?userName=Tom",
dataType:"json",
success:function (data) {
alert(data['passWord']);
},
error:function () {
alert("error");
}
});
注意我們是在 JavaWeb02 項目下的index.jsp 發(fā)送請求,請求路徑也是 JavaWeb02 下的 Servlet。
package com.ys.servlet;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Create by YSOcean
*/
@WebServlet("/ToGetPassWordServlet")
public class ToGetPassWordServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取用戶名
String userName = req.getParameter("userName");
CloseableHttpClient httpClient = HttpClients.createDefault();
//創(chuàng)建get請求
HttpGet hget = new HttpGet("http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName="+userName);
CloseableHttpResponse httpResponse = httpClient.execute(hget);
int code = httpResponse.getStatusLine().getStatusCode();
if(code == 200){
String result = EntityUtils.toString(httpResponse.getEntity());
resp.getWriter().print(result);
}
httpResponse.close();
httpClient.close();
}
}
④、nginx 轉(zhuǎn)發(fā) 原理很簡單:
利用nginx反向代理,將請求分發(fā)到部署到相應(yīng)項目的tomcat服務(wù)器,當(dāng)然也不存在跨域問題。
-
端口
+關(guān)注
關(guān)注
4文章
993瀏覽量
32388 -
瀏覽器
+關(guān)注
關(guān)注
1文章
1038瀏覽量
35736 -
javascript
+關(guān)注
關(guān)注
0文章
525瀏覽量
54125 -
腳本
+關(guān)注
關(guān)注
1文章
395瀏覽量
15049
發(fā)布評論請先 登錄
相關(guān)推薦
評論