一、對跨域的理解:
跨域問題來源于JavaScript的"同源策略",即只有 協議+主機名+埠號 相同,則允許相互訪問;也就是說JavaScript只能訪問和操作自己域下的資源,不能訪問和操作其他域下的資源,
跨域問題是為了阻止跨站攻擊,解決跨域問題其實很簡單,哪個域名要訪問我?我同意就好了,就跟我們設定白名單一樣的道理,
跨域問題是針對JS和Ajax的,html本身沒有跨域問題,
瀏覽器跨域報錯內容:
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.
非跨域:
http://www.name.com/a/b 呼叫 http://www.name.com/d/c
跨域:域名不一致
http://www.name.com/a/b 呼叫 http://www.artic.com/a/b
跨域:埠不一致
http://www.name.com:8080/a/b 呼叫 http://www.name.com:8081/d/c
跨域:協議不同
http://www.name.com/a/b 呼叫 https://www.name.com/d/c
注意:localhost和127.0.0.1雖然都指向本機,但也屬于跨域,
二、前端處理跨域問題的方法:
1、回應頭添加Header允許訪問
2、使用 jsonp方案請求( 只支持get請求不支持post請求)
3、httpClient內部轉發
4、使用介面網關——nginx
5、使用Spring Cloud zuul介面網關
(1)回應頭添加Header允許訪問
目標域(服務器)設定跨域資源共享(CORS)Cross-Origin Resource Sharing,跨域訪問解決方案的安全基礎是基于"JavaScript無法控制該HTTP頭",需要通過目標域回傳的HTTP頭來授權是否允許跨域訪問,
// 一般設定為:
response.addHeader('Access-Control-Allow-Origin:*'); //允許所有來源訪問
response.addHeader('Access-Control-Allow-Method:GET,POST'); //允許訪問的方式
// 配置串列說明
Access-Control-Allow-Origin: <origin> | * //設定能跨域訪問我的域名,其中*號代表任意域名,
Access-Control-Allow-Methods // 接受的請求方式 POST|GET...
Access-Control-Allow-Headers // 運行添加的請求頭部
Access-Control-Expose-Headers // 允許客戶端可以訪問的頭部
Access-Control-Max-Age // 本次許可的有效時長,單位是秒
Access-Control-Allow-Credentials //是否允許攜帶cookie?默認情況下值為false
(2)使用 jsonp方案請求( 只支持get請求不支持post請求)
jsonp原理:
在同源策略下,在某個服務器下的頁面是無法獲取到該服務器以外的資料的,即一般的ajax是不能進行跨域請求的,但 img、iframe 、script等標簽是個例外,這些標簽可以通過src屬性請求到其他服務器上的資料,利用 script 標簽的開放策略,我們可以實作跨域請求資料,當然這需要服務器端的配合,
ajax 和 jsonp 的核心差異:
Jquery中ajax的核心是通過 XmlHttpRequest 獲取非本頁內容,而jsonp的核心則是動態添加 script 標簽來呼叫服務器提供的 js腳本,
ajax 和 jsonp 的區別:
ajax:當我們正常地請求一個JSON資料的時候,服務端回傳的是一串JSON型別的資料,
jsonp:而我們使用 jsonp 模式來請求資料的時候服務端回傳的是一段可執行的JavaScript代碼,
jsonp為什么不支持post方法:
因為jsonp 跨域的原理就是動態加載 script 標簽的src,所以我們只能把引數通過 url 的方式傳遞,所以 jsonp 的 type 型別只能是 get !
步驟:
① 設定dataType為jsonp
② 設定服務端接收的回呼函式名(jsonp : “jsonpCallback”,),發送到后端實際為http://www.name.com/server/index?userName=644064&jsonpCallback=jQueryxxx,
然后動態加載
<script type="text/javascript" src="http://www.name.com/server/index?jsonpCallback= jQueryxxx&userName=644064"></script>
③ 后端獲取get請求中的 jsonpCallback(傳遞引數 ),把資料通過實參的形式發送出去,
④ 構造回呼函式結構
(在jquery 原始碼中, jsonp的實作方式是動態添加 script 標簽來呼叫服務器提供的 js腳本,jquery 會在window物件中加載一個全域的函式,當 script 代碼插入時函式執行,執行完畢后就 script 會被移除,同時jquery還對非跨域的請求進行了優化,如果這個請求是在同一個域名下那么他就會像正常的 Ajax請求一樣作業,)
前端請求:
$.ajax({
type : "GET",
async : false,
url : "http://www.name.com/server/index?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()+")");
(3)httpClient內部轉發:
實作原理很簡單,若想在B站點中通過Ajax訪問A站點獲取結果,固然有ajax跨域問題,但在B站點中訪問B站點獲取結果,不存在跨域問題,這種方式實際上是在B站點中ajax請求訪問B站點的HttpClient,再通過HttpClient轉發請求獲取A站點的資料結果,但這種方式產生了兩次請求,效率低,但內部請求,抓包工具無法分析,安全,
// 前端ajax請求
$.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.111.com不能直接請求www.222.com的內容,可以通過nginx,根據同域名,但專案名不同進行區分,假設目標域名為 www.test.com
通過www.test.com/A訪問,并通過nginx轉發到 www.111.com,
通過www.test.com/B訪問,并通過nginx轉發到www.222.com,
nginx 配置如下:
server {
listen 80;
server_name www.test.com;
location /A {
proxy_pass http://www.111.com:81;
index index.html index.htm;
}
location /B {
proxy_pass http://www.222.com:81;
index index.html index.htm;
}
}
原理:
我們訪問以 www.test.com 開頭且埠為80的網址,nginx 將會進行攔截匹配,若專案名為A,則分發到www.111.com:81,實際上就是通過"同源"的域名,不同的專案名進行區分,通過nginx攔截匹配,轉發到對應的網址,整個程序,兩次請求,第一次請求nginx服務器,第二次nginx服務器通過攔截匹配分發到對應的網址,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290832.html
標籤:其他
上一篇:行程、埠與套接字
