破解瀏覽器同源政策利器之JSONP

本文是在瞭解了瀏覽器的同源規則以後,學習了破解這個規則的一個簡單有效的方法->JSONP。主要經過阮一峯老師的博客學習javascript

瀏覽器的同源規則

  • 有這樣一個背景,若是你經過銀行的網站進行的取錢的交易,而其餘用戶能夠經過某個渠道得到你在銀行網站的信息,那將是很可怕的。
  • 因此,1995年NetScape公司(火狐的前身),提出了瀏覽器的同源政策,目的是保護使用網站的用戶的信息安全。那麼何謂同源呢
    • 協議相同
    • 域名相同
    • 端口號相同

不過,隨着互聯網的發展,有些時候咱們須要破解同源,因此要先學習一下,古老而有效的JSONP方法。html

瀏覽器如何向服務器提交數據

  • 有一天,程序員小白在自學,看到JSONP很牛,就向大神程序員小黑請教。小黑,小黑,這個JSONP是啥啊,感受很牛( ⊙ o ⊙ )!。java

  • 小黑扶了扶500度的眼鏡,摸了一下頭頂的幾根頭髮,如有所思的問小白。node

  • 小白啊,你說,瀏覽器怎麼向服務器提交數據啊,好比說,你要付款這個情形。jquery

  • form表單啊,我規定<form method="POST" action="/..."></form>,我不用get請求。git

    <h5>您的帳戶餘額是<span id="amount">200</span></h5>
    <button id="button">付款1塊錢</button>
    <form action="/pay" method="post">
      <input type="text" name="number" value="1">
      <input type="submit" value="付款">
    </form>
    複製代碼
  • 恩,仍是不錯的啊,知道用POST發起請求。那你這提交完了以後,是否是還要在當前頁面刷新一下,才能看到餘額啊。程序員

  • ……哎,是啊,不過我能夠給你加一個iframe,就在當前頁面刷新github

    <form action="/pay" method="post" target="result">
      <input type="text" name="number" value="1">
      <input type="submit" value="付款">
    </form>
    
    <iframe name="result" src="about:blank" frameborder="0"></iframe>
    複製代碼

    有什麼反饋信息都在iframe顯示。ajax

  • 恩,也還行,不過你爲啥要把總額200寫死在頁面呢,不該該動態從數據庫中得到嗎數據庫

  • ╮(╯▽╰)╭,稍等我改一哈

    <h5>您的帳戶餘額是<span id="amount">&&&amount&&&</span></h5>
    <button id="button">付款1塊錢</button>
    ...
    button.addEventListener('click', (e) => {
        let n = amount.innerText
        let number = parseInt(n, 10)
        let newNumber = number - 1
        amount.innerText = newNumber
    }
    複製代碼

    我用&&&amount&&&佔位符表示總額,服務器端能夠以下處理

    var amount = fs.readFileSync('./db', 'utf-8') //從db中讀取
    string = string.replace('&&&amount&&&', amount) //把佔位的數據換成真的數據
    ...
    response.write(string)
    複製代碼
  • 恩,不錯,你再想一想有沒有其餘的方式也能夠發送數據到服務器端啊,不用刷新頁面的那種

  • ……還有其餘的( ⊙ o ⊙ )啊!

  • 那我老黑我給你講講前輩程序員們試過的方法吧

    用圖片發起get請求
    let image = document.createElement('img')
    image.src = '/pay'
    image.onload = function() {
      alert('打錢成功')
      amount.innerText = amount.innerText - 1
    }
    image.onerror = function() {
      alert('打錢失敗')
    }
    複製代碼

    這種也是能夠的,並且也會用提示給用戶,交互性還能夠,不過只能發起GET請求,哈哈,我就是秀一下黑科技,不多用啦……

  • (@ο@) 哇~這也能夠,小黑,你好棒,又長見識啦,不過仍是沒給我講JSONP啊,你是否是忘了……

  • 沒忘啦,不要着急,接下來,就給你好好講講這個JSONP

動態建立JS腳本發數據

  • 小白啊,你日常用的最多的是哪門語言啊

  • 中文啊,英語不大好。

  • ……我說編程的時候

  • 呃呃,那個用的JavaScript多啊

  • 好,那我們就用js腳本發數據唄

    //用js腳本發起請求 
        let script = document.createElement('script')
        script.src = '/pay'
        document.body.appendChild(script)
        script.onerror = function() {
          alert('failed')
        }
        
       ...
       //服務器端通常這麼幹
       if(path === '/pay') {
        var amount = fs.readFileSync('./db', 'utf8')
        var newAmount = amount - 1
        
        fs.writeFileSync('./db', newAmount)
        response.setHeader('Content-Type', 'application/javascript')
        response.statusCode = 200
        response.write(` amount.innerText = amount.innerText - 1 `)
    
        response.end()
      }
    複製代碼

    以上是js腳本的大體意思,細節不要深究,明白就行。注意一下,添加script後,要記得document.body.appendChild(script)

  • 不過,小黑啊,你這動態加上了script沒錯,但是你每次都往個人html底部加js,這破壞個人html啊

  • 恩,小白啊,你思考能力仍是能夠的,目前確實有這個弊端,我給你處理一下

    //用js腳本發起請求 
        let script = document.createElement('script')
        script.src = '/pay'
        document.body.appendChild(script)
        script.onload = function(e) {
          e.currentTarget.remove() //加載完了,就移除
        }
        script.onerror = function(e) {
          alert('failed')
          e.currentTarget.remove() //加載完了,就移除
        }
    複製代碼
  • 能夠能夠,小黑你這波操做能夠的。快讓我見識見識JSONP吧

  • 好,這就給你變出來

    button.addEventListener('click', (e) => {
        //用js腳本發起請求 
        let script = document.createElement('script')
        let functionName = 'wushao' + parseInt((Math.random()*100000), 10)
        window[functionName] = function (result) {
          if (result === 'success') {
            amount.innerText = amount.innerText - 1
          } else {
          }
        }
        script.src = 'http://想訪問的另外一個網站:端口號/pay?callback=' + functionName
        document.body.appendChild(script)
        script.onload = function(e) {
          e.currentTarget.remove()
        }
        script.onerror = function(e) {
          alert('failed')
          e.currentTarget.remove()
        }
      })
     
    複製代碼
  • ヾ(。`Д´。)黑神,你這跨度有點大,咋變了了個大魔術。

  • O(∩_∩)O哈哈~,你讓我給你快點講的……,我給你講講細節吧

    1. let functionName = 'wushao' + parseInt((Math.random()*100000), 10) 使用一個隨機函數構建本身的函數名字,能夠與服務器端代碼完美解耦,服務器端只須要,得到查詢參數?callback=functionName 裏面的functionName就能夠了。

      //服務器端只須要這樣就能夠了,不關心你寫的是什麼函數名字
      response.write(` ${query.callback}.call(undefined, 'success') `)
      複製代碼
    2. window[functionName] = function (result) { } 在window全局對象上添加functionName屬性,它的值是一個函數,當服務器端響應回來後,瀏覽器端的寫的函數的參數就是服務器端的success,咱們就知道個人數據成功了。

  • 哇,厲害啊,不過你又犯了一個相同的錯誤啦,哈哈,每次要把添加的全局對象的屬性去掉哦~

    script.onload = function(e) {
      e.currentTarget.remove()
      delete window[functionName]
    }
    script.onerror = function(e) {
      alert('failed')
      e.currentTarget.remove()
      delete window[functionName]
    }
    複製代碼
  • O(∩_∩)O哈!,這樣子就對了,小白啊,既然你學過jQuery,你試一試jQuery的寫法吧

  • (^o^)/~行,小黑,我也給你變一個

    $.ajax({
      url: "http://想訪問的另外一個網站:端口號/pay",
    
      // The name of the callback parameter, as specified by the YQL service
      jsonp: "callback",
    
      // Tell jQuery we're expecting JSONP
      dataType: "jsonp",
    
      // Work with the response
      success: function (response) {
        if(response === 'success') {
        amount.innerText = amount.innerText - 1 
        }  
      }
    })
    複製代碼
  • 哎呦,不錯呦,小白~

  • O(∩_∩)O哈哈~,我就是Google的 jquery jsonp

  • 不過,這個可和ajax,沒啥關係啊,不明白爲啥jquery爲啥這麼寫。

具體的代碼連接在============>傳送門

什麼是JSONP呢

請求方是一個網站(瀏覽器端),響應方是另外一個網站(服務器端)

  1. 請求方動態的建立一個script腳本,src屬性是響應方的地址,同時傳遞一個查詢查參數?callback=functionName,通常functionName使用隨機函數構造。

  2. 響應方根據收到的查詢參數callback=functionName,去構造形如

    2.1 functionName.call(undefined, 'success')

    2.2 或者直接functionName.('success')

    這樣的響應。

  3. 瀏覽器收到響應以後,就會執行functionName.call(undefined, 'success')或者functionName.('success')

  4. 而後,請求方就知道了他想要得到的數據如何了。

這就是JSONP的原理

爲何JSONP不支持POST請求呢

答曰:JSONP是動態建立的js腳本,這個方法只能發起GET請求,不能發起POST請求。

接下來學習ajax啦~加油↖(^ω^)↗

相關文章
相關標籤/搜索