關於前后端分離跨域請求問題


1.問題描述:

  在使用react開發時遇到了前后端分離post請求跨域的問題,導致請求無法正常完成。

2.什么是跨域?

  當客戶端向服務器發起一個網絡請求,url會有包含三個主要信息:協議(protocol),域名(host),端口號(port)。當三部分都和服務器相同的情況下,屬於同源。但是只要有一個不同,就屬於構成了跨域調用。會受到同源策略的限制。

  同源策略限制從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。瀏覽器的同源策略,出於防范跨站腳本的攻擊,禁止客戶端腳本(如 JavaScript)對不同域的服務進行跨站調用(通常指使用XMLHttpRequest請求)。

3.幾種跨域解決方式:

3.1. jsonp請求:

  JSONP(json with padding 填充式json),利用了使用src引用靜態資源時不受跨域限制的機制。主要在客戶端搞一個回調做一些數據接收與操作的處理,並把這個回調函數名告知服務端,而服務端需要做的是按照javascript的語法把數據放到約定好的回調函數之中即可。jQuery很早之前就已經吧JSONP語法糖化了,使用起來會更加方便。

  jsonp的簡單實現:

 1 // 回調函數
 2 function jsonpCallback(data) {
 3     console.log("jsonpCallback: " + data.name)
 4 }
 5 $("#submit").click(function() {
 6     var data = {
 7         name: $("#name").val(),
 8         id: $("#id").val()
 9     };
10     $.ajax({
11         url: 'http://localhost:3001/ajax/deal',
12         data: data,
13         dataType: 'jsonp',
14         cache: false,
15         timeout: 5000,
16         // jsonp 字段含義為服務器通過什么字段獲取回調函數的名稱
17         jsonp: 'callback',
18         // 聲明本地回調函數的名稱,jquery 默認隨機生成一個函數名稱
19         jsonpCallback: 'jsonpCallback',
20         success: function(data) {
21             console.log("ajax success callback: " + data.name)
22         },
23         error: function(jqXHR, textStatus, errorThrown) {
24             console.log(textStatus + ' ' + errorThrown);
25         }
26     });
27 });

 

3.2 cors跨域資源共享

  CORS(Cross-origin resource sharing 跨域資源共享),依附於AJAX,通過添加HTTP Hearder部分字段請求與獲取有權限訪問的資源。CORS對開發者是透明的,因為瀏覽器會自動根據請求的情況(簡單和復雜)做出不同的處理。CORS的關鍵是服務端的配置支持。由於CORS是W3C中一項較“新”的方案,以至於各大網頁解析引擎還沒有對其進行嚴格規格的實現,所以不同引擎下可能會有一些不一致。

  (1)Access-Control-Allow-Origin

該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*,表示接受任意域名的請求。

  (2)Access-Control-Allow-Credentials

該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設為true,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。如果要發送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁代碼中的document.cookie也無法讀取服務器域名下的Cookie。

  (3)Access-Control-Expose-Headers

該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

  java過濾器實現cors:

 1 public class CorsFilter implements Filter {
 2 
 3     public void init(FilterConfig filterConfig) throws ServletException {
 4     }
 5 
 6     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 7         throws IOException, ServletException {
 8         HttpServletResponse httpResponse = (HttpServletResponse) response;
 9         httpResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");//設置允許跨域的域名,需要發送cookie信息,所以此處需要指定具體的域名,
10         httpResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
11         httpResponse.setHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE, POST");//允許跨域的請求方式
12         /**
13          * ajax請求的時候如果帶有xhrFields:{withCredentials:true},
14          * 那么服務器后台在配置跨域的時候就必須要把Access-Control-Allow-Credentials這個請求頭加上去
15          */
16         httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//允許發送Cookie信息
17         httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // 支持HTTP 1.1.
18         httpResponse.setHeader("Pragma", "no-cache"); // 支持HTTP 1.0. response.setHeader("Expires", "0");
19         chain.doFilter(request, response);
20     }
21 
22     public void destroy() {
23         // TODO Auto-generated method stub
24     }
25 }

 

jsonp和cors的不同:

  兩者優點與缺點大致互補,放在一塊介紹:

  1. JSONP的主要優勢在於對瀏覽器的支持較好;雖然目前主流瀏覽器支持CORS,但IE10以下不支持CORS。
  2. JSONP只能用於獲取資源(即只讀,類似於GET請求);CORS支持所有類型的HTTP請求,功能完善。(這點JSONP被玩虐,但大部分情況下GET已經能滿足需求了)
  3. JSONP的錯誤處理機制並不完善,我們沒辦法進行錯誤處理;而CORS可以通過onerror事件監聽錯誤,並且瀏覽器控制台會看到報錯信息,利於排查。
  4. JSONP只會發一次請求;而對於復雜請求,CORS會發兩次請求
  5. 始終覺得安全性這個東西是相對的,沒有絕對的安全,也做不到絕對的安全。畢竟JSONP並不是跨域規范,它存在很明顯的安全問題:callback參數注入和資源訪問授權設置。CORS好歹也算是個跨域規范,在資源訪問授權方面進行了限制(Access-Control-Allow-Origin),而且標准瀏覽器都做了安全限制,比如拒絕手動設置origin字段,相對來說是安全了一點。
    但是回過頭來看一下,就算是不安全的JSONP,我們依然可以在服務端端進行一些權限的限制,服務端和客戶端也都依然可以做一些注入的安全處理,哪怕被攻克,它也只能讀一些東西。就算是比較安全的CORS,同樣可以在服務端設置出現漏洞或者不在瀏覽器的跨域限制環境下進行攻擊,而且它不僅可以讀,還可以寫。

3.3. react通過webpack配置代理實現跨域請求:

  代理的話其實也可以使用nginx反向代理,但是配置的時候發現請求過程總是處於(pending)等待狀態,很久才能響應,不知是何原因引起,還未解決,如有大牛看到,肯請指教。

nginx配置如下:

 1 location ^~/ {
 2              proxy_pass http://localhost:8080;
 3              proxy_redirect default ;
 4              proxy_set_header Host $host:80;
 5              proxy_set_header X-Real-IP $remote_addr;
 6              proxy_set_header REMOTE-HOST $remote_addr;
 7              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 8              
 9             
10         }

  因為失敗了,所以轉而使用webpack代理配置:

  首先在webpack.config.js中進行配置:

module.exports = {
  //。。。省略

devServer: {
            port: '8081',
            host: '127.0.0.1',
            historyApiFallback: false,
            disableHostCheck: true,
            noInfo: false,
            stats: 'minimal',
            inline: true,
            //開啟服務器的模塊熱替換(HMR)
            hot: true,
            // 和上文 output 的“publicPath”值保持一致
            publicPath: context,
            proxy: { '/sharemeeting/*': { target: "http://localhost:8080", changeOrigin: true, secure: false } }
        }
    //。。。省略
};

 

 

   proxy中,'/sharemeeting/*'是過濾要代理的請求的路徑,tartget為代理后的目標服務器地址,包括協議、域名、端口號信息。如果發起的請求為http://localhost:8081/sharemeeting/getTest.do。那么將會被代理為http://localhost:8080/sharemeeting/getTest.do

  fetch請求

 

 1       let url = "/sharemeeting/login/getTest.do";
 2         fetch(url,{
 3             method: 'POST',
 4             headers: {
 5                 'Accept': 'application/json',
 6                 'Content-Type': 'application/json'
 7             },
 8             credentials: 'include',
 9             body: JSON.stringify({})
10         }).then((response) => response.json()) //把response轉為json
11           .then((responseData) => { // 上面的轉好的json
12                 if(responseData.status === false){
13                     window.location.href="#/";
14                 }else{
15                     this.reset(responseData.data);
16                 }
17             }).catch((error)=> {
18             window.location.href="#/";
19         })

 

 

 

  請求的url要像上面這樣定義,如果這樣定義,這樣請求的時候會自己添加上協議和請求的域名端口號,

 

 

======================================參考=================================

1.https://www.zhihu.com/question/41992168 JS跨域方案JSONP與CORS的各自優缺點以及應用場景?

2.http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html 瀏覽器同源政策及其規避方法

3.http://www.ruanyifeng.com/blog/2016/04/cors.html 跨域資源共享 CORS 詳解

4.https://www.jianshu.com/p/3bdff821f859 Webpack dev server使用http-proxy解決跨域問題

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM