跳至主要內容

02.浏览器的同源策略

pinia大约 3 分钟

简版

  • 浏览器处于安全考虑,对同源请求放行,对异源请求限制。这些限制规则统称为同源策略。
  • 因这些限制问题造成的开发问题,称之为跨域(异源)问题。

同源

同源 = 协议 + 域名 + 端口号

同源请求

同源请求 = 页面源和目标源相一致的情况下

同源策略

  • 浏览器如何限制异源请求?
    • 对标签发出的跨域请求轻微限制 link script img video audio
    • 对AJAX发出的跨域请求严厉限制

对AJAX的限制

  • 用户用XHR或者fetch对浏览器发出跨域请求,浏览器对服务器发出请求
  • 服务器响应浏览器的请求,浏览器同时进行校验,通过则交付,不通过则限制(引发错误,跨域问题)

跨域问题的解决方案

CORS(Cross-Origin Resource Sharing)

  • 在浏览器对服务器的响应校验时,校验有校验规则CORS

  • 规则

    1. CORS是一套机制,用于浏览器校验跨域请求
    2. 它的基本理念是
      1. 服务器只要明确表示允许,则校验通过
      2. 服务器明确表示拒绝或没有表示,则校验不通过
    3. 使用CORS,必须确保服务器是自己的服务器,别人的服务器是不行的
  • CORS把请求分为俩类

    • 简单请求
      • 请求的主要方式有:GET HEAD POST
      • 头部字段满足CORS安全规范,详见[W3C](Fetch Standard (whatwg.org)open in new window)
        • 改变头部就是不满足CORS安全规范
      • 请求头的Content-Type为
        • text/plain
        • multipart/form-data
        • application/x-www-form-urlcoded
    • 预检请求
      • 非简单请求
  • 简单请求:服务器对浏览器响应是添加一个响应头

    Access-Control-Allow-Origin:* //或者是浏览器向服务器发出请求时,请求头里的Origin里的源
    
  • 预检请求

    • 浏览器向服务器发送预检请求
    Origin:源
    Access-Control-Resquest-Method:POST
    Access-Control-Resquest-Headers:a,b,content-type
    
    • 服务器对浏览器发出响应
    Access-Control-Allow-Origin:* //或者是浏览器向服务器发出请求时,请求头里的Origin里的源
    Access-Control-Allow-Method:POST
    Access-Control-Allow-Headers:a,b,content-type
    Access-Control-Max-Age:86400
    

JSONP(JSON with Padding)

  • JSONP是解决跨域问题的古老方案。同源策略中对标签的跨域请求限制较小,JSONP就是利用了这一点

  • JSONP只有get请求

  • 方案

    • 创建script元素并发送跨域请求
    <script>
      /**
       * 准备一个回调函数
       * 服务器响应后会运行这个函数
       * 并传递响应数据给参数
       * @param jsonp
       */
      function callback(jsonp) {
        console.log(jsonp)
      }
      function request(url){
        const script = document.createElement('script')
        script.src = url
        script.onload = function () {
          this.remove()
        }
        document.body.appendChild(script)
      }
      document.querySelector('button').onclick = function () {
        request('http://localhost:3000/say?wd=hello&callback=callback')
      }
    </script>
    
    • 服务器响应结果是一个函数调用
    callback([1,2,3])
    

注意

CORS和JSONP均对服务器有要求

代理

  • 方案
    • Proxy是自己的服务器
    • 请求:浏览器跨域请求Proxy代理服务器,Proxy代理服务器转发请求到target目标服务器
    • 响应:target目标服务器对Proxy服务器发出响应,Proxy代理服务器通过CORS或者是JSONP对浏览器发出响应
<script>
  /**
   * 代理服务器
   */
  const express = require('express')
  const app = express()

  //接受对路劲为/api的请求
  app.get('/api', async (req, res) => {
    //使用CORS解决对代理服务器的跨域问题
    const axios = require('axios')
    const resp = await axios.get('https://pvp.qq.com/web201605/js/herolist.json')
    res.header('Access-Control-Allow-Origin', '*')
    //返回数据
    res.send(resp.data)
  })
  //监听3000端口
  app.listen(3000, () => {
    console.log('server is running at port 3000')
  })
</script>

使用场景

  • 能改变服务器吗?
      • 浏览器支持CORS吗?
        • 支持 CORS
        • 不支持 JSONP
    • 不能
      • 代理
上次编辑于:
贡献者: 林深不见鹿