美文网首页
同源策略、跨域

同源策略、跨域

作者: zooeydotmango | 来源:发表于2019-09-25 01:39 被阅读0次

    同源策略

    浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指,域名,协议,端口相同。不同源的客户端脚(javascript、ActionScript)本在没明确授权的情况下,不能读写对方的资源。
    在这种情况下,域名、协议、端口有其中之一不同的域向服务器ajax请求数据,即使服务器响应了也会被浏览器拦截

    同源策略限制内容有:

    • Cookie、LocalStorage、IndexedDB 等存储性内容
    • DOM 节点
    • AJAX 请求发送后,结果被浏览器拦截了

    但是有三个标签是允许跨域加载资源:

    • <img src=XXX>
    • <link href=XXX>
    • <script src=XXX>

    跨域

    在有同源策略的情况下如果我们还是要这么操作,就需要跨域

    降域

    父域名与子域名可以通过降域使浏览器认为他们是同一个源

    child1.a.com  
    a.com
    document.domain = 'a.com'  //降域
    

    jsonp

    • 原理:利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
    • JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。
    • 实现流程:
      1. 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
      2. 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
      3. 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')。
      4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。
    <script src="http://api.jirengu.com/weather.php?callback=showData"></script>
    //返回
    showData({“city”: “hangzhou”, “weather”: “晴天”})
    

    这样我们只要在前端提前定义好showData就可以使用传回来的数据

    跨域资源共享CORS(Cross-origin resource sharing)

    浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
    服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
    虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。

    1. 简单请求
      满足两大条件就属于简单请求

    请求方法是以下三种方法之一:
    - GET
    - HEAD
    - POST

    HTTP的头信息不超出以下几种字段:
    - Accept
    - Accept-Language
    - Content-Language
    - Last-Event-ID
    - Content-Type 的值仅限于下列三者之一:ext/plain,multipart/form-data,application/x-www-form-urlencoded

    //后端
    // 处理成功失败返回格式的工具
    const {successBody} = require('../utli')
    class CrossDomain {
      static async cors (ctx) {
        const query = ctx.request.query
        // *时cookie不会在http请求中带上
        ctx.set('Access-Control-Allow-Origin', '*')
        ctx.cookies.set('tokenId', '2')
        ctx.body = successBody({msg: query.msg}, 'success')
      }
    }
    module.exports = CrossDomain
    

    前端正常发请求

    fetch(`http://localhost:9871/api/cors?msg=helloCors`).then(res => {
      console.log(res)
    })
    
    1. 非简单请求
      非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。
      后端
    const path = require('path')
    const Koa = require('koa')
    const koaStatic = require('koa-static')
    const bodyParser = require('koa-bodyparser')
    const router = require('./router')
    const cors = require('koa2-cors')
    const app = new Koa()
    const port = 9871
    app.use(bodyParser())
    // 处理静态资源 这里是前端build好之后的目录
    app.use(koaStatic(
      path.resolve(__dirname, '../dist')
    ))
    // 处理cors
    app.use(cors({
      origin: function (ctx) {
        return 'http://localhost:9099'
      },
      credentials: true,
      allowMethods: ['GET', 'POST', 'DELETE'],
      allowHeaders: ['t', 'Content-Type']
    }))
    // 路由
    app.use(router.routes()).use(router.allowedMethods())
    // 监听端口
    app.listen(9871)
    console.log(`[demo] start-quick is starting at port ${port}`)
    

    前端

    fetch(`http://localhost:9871/api/cors?msg=helloCors`, {
      // 需要带上cookie
      credentials: 'include',
      // 这里添加额外的headers来触发非简单请求
      headers: {
        't': 'extra headers'
      }
    }).then(res => {
      console.log(res)
    })
    

    postMessage

    同源策略限制下Dom查询的正确打开方式,window.postMessage() 是HTML5的一个接口,专注实现不同窗口不同页面的跨域通讯。
    为了演示方便,我们将hosts改一下:127.0.0.1 crossDomain.com,现在访问域名crossDomain.com就等于访问127.0.0.1。
    这里是http://localhost:9099/#/crossDomain,发消息方

    <template>
      <div>
        <button @click="postMessage">给http://crossDomain.com:9099发消息</button>
        <iframe name="crossDomainIframe" src="http://crossdomain.com:9099"></iframe>
      </div>
    </template>
    
    <script>
    export default {
      mounted () {
        window.addEventListener('message', (e) => {
          // 这里一定要对来源做校验
          if (e.origin === 'http://crossdomain.com:9099') {
            // 来自http://crossdomain.com:9099的结果回复
            console.log(e.data)
          }
        })
      },
      methods: {
        // 向http://crossdomain.com:9099发消息
        postMessage () {
          const iframe = window.frames['crossDomainIframe']
          iframe.postMessage('我是[http://localhost:9099], 麻烦你查一下你那边有没有id为app的Dom', 'http://crossdomain.com:9099')
        }
      }
    }
    </script>
    

    这里是http://crossdomain.com:9099,接收消息方

    <template>
      <div>
        我是http://crossdomain.com:9099
      </div>
    </template>
    
    <script>
    export default {
      mounted () {
        window.addEventListener('message', (e) => {
          // 这里一定要对来源做校验
          if (e.origin === 'http://localhost:9099') {
            // http://localhost:9099发来的信息
            console.log(e.data)
            // e.source可以是回信的对象,其实就是http://localhost:9099窗口对象(window)的引用
            // e.origin可以作为targetOrigin
            e.source.postMessage(`我是[http://crossdomain.com:9099],我知道了兄弟,这就是你想知道的结果:${document.getElementById('app') ? '有id为app的Dom' : '没有id为app的Dom'}`, e.origin);
          }
        })
      }
    }
    </script>
    

    结果可以看到


    image.png

    相关文章

      网友评论

          本文标题:同源策略、跨域

          本文链接:https://www.haomeiwen.com/subject/cbpaectx.html