跨域問題及解決方案


1. 什么是跨域

  • 跨域帶來的風險?

    • 跨域請求和Ajax技術都會極大地提高頁面的體驗,但同時也會帶來安全的隱患,其中最主要的隱患來自於CSRF(Cross-site request forgery)跨站請求偽造。

    • csrf攻擊的大致原理

      假如一家銀行用以運行轉賬操作的URL地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName

      那么,一個惡意攻擊者可以在另一個網站上放置一張不可描述的圖片引誘你點擊,點擊圖片后向"http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman"發送請求

      如果有賬戶名為Alice的用戶訪問了惡意站點,而她之前剛訪問過銀行不久,登錄信息尚未過期,那么她就會損失1000資金。

  • 跨域是指我們訪問一個網站,如:http://127.0.0.1:8000這個url,從這個頁面中又去訪問http://127.0.0.1:9000這個url,這個時候就引發了跨域,當域名、端口、二級域名不同都會引發跨域。 此時9000端口的服務端可以接收到請求,也會給瀏覽器響應數據,但是到達瀏覽器后就被攔截了,因為瀏覽器的同源策略。

    URL 結果 原因
    http://www.cnblogs.com/liuweida 成功 域名、協議、端口相同
    https://www.cnblogs.com/liuweida 失敗 協議不同
    http://www.cnblogs.com:8888/liuweida 失敗 端口不同
    http://www.cnblogs.cn/liuweida 失敗 域名不同

2. 什么是同源策略?

  • 同源策略是一種約定,它是瀏覽器最核心也會是最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。它約定請求的url地址,必須與瀏覽器的url地址處於同域上,也就是域名,端口,協議都相同。如果不同,就會報錯:

  • 實際上的結果是,請求已經被發送過去了,目標服務器也對請求做出了響應,只是瀏覽器對非同源請求的返回結果做了攔截。

一些內嵌資源不受限制

如:

  • <script src="..."></script> 標簽嵌入跨域腳本。語法錯誤信息只能在同源腳本中捕捉到。
  • <link rel="stylesheet" href="..."> 標簽嵌入CSS。
  • <img> 嵌入圖片。
  • <video><audio> 嵌入多媒體資源。
  • <object>, <embed><applet>的插件。
  • @font-face 引入的字體。一些瀏覽器允許跨域字體( cross-origin fonts),一些需要同源字體(same-origin fonts)。
  • <frame><iframe>載入的任何資源。站點可以使用X-Frame-Options消息頭來阻止這種形式的跨域交互。

限制范圍

非同源的網站,主要有3種行為受到限制

  1. 無法共享 cookie, localStorage, indexDB
  2. 無法操作彼此的 DOM 元素
  3. 無法發送 Ajax 請求

3. 如何解決跨域發送請求問題

3.1 jsonp

  • jsonp 全稱是JSON with Padding,是為了解決跨域請求資源而產生的解決方案,是一種依靠開發人員創造出的一種非官方跨域數據交互協議。

  • 基於script標簽實現的跨域

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
     
    <script type="text/javascript">
        var messagetow = function(data){
            alert(data);
        };
        var url = "http://192.168.31.137/train/test/jsonpthree?callback=messagetow";
        var script = document.createElement('script');
        script.setAttribute('src', url);
        document.getElementsByTagName('body')[0].appendChild(script);
    </script>
    </head>
    <body>
    </body>
    </html>
    
  • 基於jquery跨域

    $.ajax({
         type : 'get',
         url:'http://192.168.31.137/train/test/testjsonp',
         data : {
            name : name,
            sex : sex,
            address : address,
            looks : looks,
        },
        cache :false,
        jsonp: "callback",
        jsonpCallback:"success",
        dataType : 'jsonp',
        success:function(data){
            alert(data);
        },
        error:function(data){
            alert('error');
        }       
    });
    

3.2 cors[推薦]

  • CORS(Cross-origin resource sharing,跨域資源共享)是一個 W3C 標准,定義了在必須訪問跨域資源時,瀏覽器與服務器應該如何溝通(需要客戶端和服務端協同處理)
  • CORS有兩種請求,簡單請求和非簡單請求。

簡單請求

  • 什么是簡單請求?

    只要同時滿足以下兩大條件,就屬於簡單請求。
    
    (1)請求方法是以下三種方法之一:
    	- HEAD
    	- GET
    	- POST
    (2)HTTP的頭信息不超出以下幾種字段:
    	- Accept
    	- Accept-Language
    	- Content-Language
    	- Last-Event-ID
    	- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
    
  • 簡單請求實現cors

    1.前端不需要進行特殊的操作
    2.后端需要加入請求頭Access-Control-Allow-Origin,代碼如下:
        def get_time(request):
            if request.method == 'GET':
                ntime = time.strftime('%Y-%m-%d %X')
                obj = HttpResponse(ntime)
                obj['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
                return obj
    

復雜請求

  • 兩次請求,在發送數據之前會先發第一次請求做預檢,只有預檢通過后在發一次請求作為數據傳輸。

    - 請求方式:OPTIONS
    - “預檢”其實做檢查,檢查如果通過則允許傳輸數據,檢查不通過則不再發送真正想要發送的消息
    - 如何“預檢”
    	=> Access-Control-Allow-Origin(必含)
    	   設置為'*',允許所有域名訪問
    	   一般設置為指定域名,如:'Access-Control-Allow-Origin:http://192.168.12.87'
    	=> 如果復雜請求是PUT等請求,則服務端需要設置允許某請求,否則“預檢”不通過
            Access-Control-Request-Method
    	=> 如果復雜請求設置了請求頭,則服務端需要設置允許某請求頭,否則“預檢”不通過
            Access-Control-Request-Headers
    
  • 復雜請求實現

    前端

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <button id="a" class="btn btn-success">go</button>
    <script>
        $("#a").click(function () {
            $.ajax({
                url:'http://127.0.0.1:8000/money/',
                type:'post',
                contentType:'application/json',
                data:{"name":"yjh"},
                success:function (data) {
                    alert(data)
                }
                }
            )
        })
    </script>
    </body>
    </html>
    

    后台

    from django.http import QueryDict
    def get_money(request,response):
        if request.method == 'OPTIONS':
            response['Access-Control-Allow-Origin'] = '*' # 允許所有域名訪問,一般會設置成指定的域名
            response['Access-Control-Allow-Headers'] = 'Content-Type'  # 復雜請求添加的頭信息
        response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
        return response
    

全局配置

  • 全局配置可以寫在中間件中,瀏覽器是接收到返回數據的時候出現禁用,所以在數據返回時添加響應頭信息即可,所以重寫中間件中process_response方法

  • 步驟一:在py文件中寫如下代碼:

    from django.utils.deprecation import MiddlewareMixin
    class CorsMiddleWare(MiddlewareMixin):
        def process_response(self,request,response):
            if request.method=="OPTIONS":
                response['Access-Control-Allow-Origin'] = 'http://localhost' 
                response["Access-Control-Allow-Headers"]="Content-Type"
            response["Access-Control-Allow-Origin"] = "http://localhost:8080"
            return response
    
  • 步驟二:在配置文件settings.py中添加注冊中間件

    MIDDLEWARE = [
        'app01.my_middleware.CorsMiddleware',  # 在第一行寫是因為django中間件的執行順序,process_response倒序執行
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    


免責聲明!

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



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