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

本文是在了解了瀏覽器的同源規(guī)則之后,并學(xué)習(xí)一個破解這個規(guī)則的一個簡單有效的方法JSONP。主要通過阮一峰老師的博客學(xué)習(xí)

瀏覽器的同源規(guī)則

  • 有這樣一個背景,如果你通過銀行的網(wǎng)站進(jìn)行的取錢的交易,而其他用戶可以通過某個渠道獲得你在銀行網(wǎng)站的信息,那將是很可怕的。
  • 所以,1995年NetScape公司(火狐的前身),提出了瀏覽器的同源政策,目的是保護(hù)使用網(wǎng)站的用戶的信息安全。那么何謂同源呢
    • 協(xié)議相同
    • 域名相同
    • 端口號相同

不過,隨著互聯(lián)網(wǎng)的發(fā)展,有些時候我們需要破解同源,所以要先學(xué)習(xí)一下,古老而有效的JSONP方法。

瀏覽器如何向服務(wù)器提交數(shù)據(jù)

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

  • 小黑扶了扶500度的眼鏡,摸了一下頭頂?shù)膸赘^發(fā),若有所思的問小白。

  • 小白啊,你說,瀏覽器怎么向服務(wù)器提交數(shù)據(jù)啊,比如說,你要付款這個情形。

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

    <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發(fā)起請求。那你這提交完了之后,是不是還要在當(dāng)前頁面刷新一下,才能看到余額啊。

  • ……哎,是啊,不過我可以給你加一個iframe,就在當(dāng)前頁面刷新

    <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顯示。

  • 恩,也還行,不過你為啥要把總額200寫死在頁面呢,不應(yīng)該動態(tài)從數(shù)據(jù)庫中獲得嗎

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

    <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&&&占位符表示總額,服務(wù)器端可以如下處理

    var amount = fs.readFileSync('./db', 'utf-8') //從db中讀取
    string = string.replace('&&&amount&&&', amount) //把占位的數(shù)據(jù)換成真的數(shù)據(jù)
    ...
    response.write(string)
    
  • 恩,不錯,你再想想有沒有其他的方式也可以發(fā)送數(shù)據(jù)到服務(wù)器端啊,不用刷新頁面的那種

  • ……還有其他的( ⊙ o ⊙ )??!

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

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

    這種也是可以的,而且也會用提示給用戶,交互性還可以,不過只能發(fā)起GET請求,哈哈,我就是秀一下黑科技,很少用啦……

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

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

動態(tài)創(chuàng)建JS腳本發(fā)數(shù)據(jù)

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

  • 中文啊,英語不大好。

  • ……我說編程的時候

  • 呃呃,那個用的JavaScript多啊

  • 好,那咱們就用js腳本發(fā)數(shù)據(jù)唄

      //用js腳本發(fā)起請求  
        let script = document.createElement('script')
        script.src = '/pay'
        document.body.appendChild(script)
        script.onerror = function() {
          alert('failed')
        }
        
       ...
       //服務(wù)器端一般這么干
       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腳本的大致意思,細(xì)節(jié)不要深究,明白就行。注意一下,添加script后,要記得document.body.appendChild(script)

  • 不過,小黑啊,你這動態(tài)加上了script沒錯,可是你每次都往我的html底部加js,這破壞我的html啊

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

        //用js腳本發(fā)起請求  
        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() //加載完了,就移除
        }
    
  • 可以可以,小黑你這波操作可以的??熳屛乙娮R見識JSONP吧

  • 好,這就給你變出來

     button.addEventListener('click', (e) => {
        //用js腳本發(fā)起請求  
        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://想訪問的另一個網(wǎng)站:端口號/pay?callback=' + functionName
        document.body.appendChild(script)
        script.onload = function(e) {
          e.currentTarget.remove()
        }
        script.onerror = function(e) {
          alert('failed')
          e.currentTarget.remove()
        }
      })
     
    
  • ヾ(?`Д′?)黑神,你這跨度有點大,咋變了了個大魔術(shù)。

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

    1. let functionName = 'wushao' + parseInt((Math.random()*100000), 10) 使用一個隨機函數(shù)構(gòu)建自己的函數(shù)名字,可以與服務(wù)器端代碼完美解耦,服務(wù)器端只需要,獲得查詢參數(shù)?callback=functionName 里面的functionName就可以了。

      //服務(wù)器端只需要這樣就可以了,不關(guān)心你寫的是什么函數(shù)名字
      response.write(`
         ${query.callback}.call(undefined, 'success')
      `)
      
    2. window[functionName] = function (result) { } 在window全局對象上添加functionName屬性,它的值是一個函數(shù),當(dāng)服務(wù)器端響應(yīng)回來后,瀏覽器端的寫的函數(shù)的參數(shù)就是服務(wù)器端的success,我們就知道我的數(shù)據(jù)成功了。

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

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

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

    $.ajax({
      url: "http://想訪問的另一個網(wǎng)站:端口號/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,沒啥關(guān)系啊,不明白為啥jquery為啥這么寫。

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

什么是JSONP呢

請求方是一個網(wǎng)站(瀏覽器端),響應(yīng)方是另一個網(wǎng)站(服務(wù)器端)

  1. 請求方動態(tài)的創(chuàng)建一個script腳本,src屬性是響應(yīng)方的地址,同時傳遞一個查詢查參數(shù)?callbackName=functionName。對于callbackNamefunctionName,通常使用callback來代替callbackName,使用一串具有特殊含義的字母+隨機函數(shù)構(gòu)造functionName。

  2. 響應(yīng)方根據(jù)收到的查詢參數(shù)callbackName=functionName,去構(gòu)造形如

    2.1 functionName.call(undefined, 'success')

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

    這樣的響應(yīng)。

  3. 瀏覽器收到響應(yīng)之后,就會執(zhí)行functionName.call(undefined, 'success')或者functionName.('success')

  4. 然后,請求方就知道了他想要獲得的數(shù)據(jù)如何了。

這就是JSONP的原理

為什么JSONP不支持POST請求呢

答曰:

  1. JSONP通過動態(tài)創(chuàng)建script來實現(xiàn)的
  2. 動態(tài)創(chuàng)建script的時候,只能用get,沒辦法用post

接下來學(xué)習(xí)ajax啦~加油↖(ω)↗

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • JSONP 問:什么是JSONP? 1. 問題引入 在日常開發(fā)中,前端程序員想要從后端請求一些數(shù)據(jù),是如何操作的呢...
    黃同學(xué)2019閱讀 500評論 0 2
  • 題目1: 什么是同源策略 同源策略限制了從一個源加載的文檔或腳本如何與來自另一個源的資源進(jìn)行交互。這是一個用于隔離...
    饅頭Mum閱讀 1,674評論 0 1
  • AJAX 原生js操作ajax 1.創(chuàng)建XMLHttpRequest對象 var xhr = new XMLHtt...
    碧玉含香閱讀 3,544評論 0 7
  • 堅持原創(chuàng)分享第78天 以往提到學(xué)習(xí),在我腦海中的印象就是教室里、臺燈下,再文藝一點就是小河邊、柳樹下,但是...
    我是一只沒有腳的鳥閱讀 431評論 0 1

友情鏈接更多精彩內(nèi)容