开发者社区> 问答> 正文

如何实现跨域?了解预检请求嘛?

展开
收起
前端问答 2019-12-12 07:54:46 1611 0
1 条回答
写回答
取消 提交回答
  • 前端问答小助手

    跨域是个⽐较古⽼的命题了,历史上跨域的实现⼿段有很多,我们现在主要介绍三种⽐较主流的跨域⽅案,其余的⽅案我们就不深⼊讨论了,因为使⽤场景很少,也没必要记这么多奇技淫巧。

    最经典的跨域⽅案jsonp

    jsonp本质上是⼀个Hack,它利⽤ <script>标签不受同源策略限制的特性进⾏跨域操作。

    jsonp优点:

    • 实现简单
    • 兼容性⾮常好 jsonp的缺点:

    • 只⽀持get请求(因为 <script> 标签只能get)

    • 有安全性问题,容易遭受xss攻击
    • 需要服务端配合jsonp进⾏⼀定程度的改造

    jsonp的实现:

    function JSONP({
    url,
    params,
    callbackKey,
    callback
    }) {
    // 在参数⾥制定 callback 的名字
    params = params || {}
    params[callbackKey] = 'jsonpCallback'
    // 预留 callback
    window.jsonpCallback = callback
    // 拼接参数字符串
    const paramKeys = Object.keys(params)
    const paramString = paramKeys
    .map(key => `${key}=${params[key]}`)
    .join('&')
    // 插⼊ DOM 元素
    const script = document.createElement('script')
    script.setAttribute('src', `${url}?${paramString}`)
    document.body.appendChild(script)
    }
    JSONP({
    url: 'http://s.weibo.com/ajax/jsonp/suggestion',
    params: {
    key: 'test',
    },
    callbackKey: '_cb',
    callback(result) {
    console.log(result.data)
    }
    })
    

    最流⾏的跨域⽅案cors

    cors是⽬前主流的跨域解决⽅案,跨域资源共享(CORS) 是⼀种机制,它使⽤额外的 HTTP 头来告诉浏览器 让运⾏在⼀个 origin (domain) 上的Web应⽤被准许访问来⾃不同源服务器上的指定的资源。当⼀个资源从与该资源本身所在的服务器不同的域、协议或端⼝请求⼀个资源时,资源会发起⼀个跨域 HTTP 请求。

    如果你⽤express,可以这样在后端设置

    //CORS middleware
    var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://example.com');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    next();
    }
    //...
    app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({ secret: 'cool beans' }));
    app.use(express.methodOverride());
    app.use(allowCrossDomain);
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
    });
    

    在⽣产环境中建议⽤成熟的开源中间件解决问题。

    简单请求

    以 Ajax 为例,当满足以下条件时,会触发简单请求

    使用下列方法之一:

    GET
    
    HEAD
    
    POST
    
    Content-Type 的值仅限于下列三者之一:
    
    text/plain
    
    multipart/form-data
    
    application/x-www-form-urlencoded
    
    

    请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

    复杂请求

    那么很显然,不符合以上条件的请求就肯定是复杂请求了。

    对于复杂请求来说,首先会发起一个预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

    对于预检请求来说,如果你使用过 Node 来设置 CORS 的话,可能会遇到过这么一个坑。

    以下以 express 框架举例:

    app.use((req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*')
      res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
      res.header(
        'Access-Control-Allow-Headers',
        'Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials'
      )
      next()
    })
    
    

    该请求会验证你的 Authorization 字段,没有的话就会报错。

    当前端发起了复杂请求后,你会发现就算你代码是正确的,返回结果也永远是报错的。因为预检请求也会进入回调中,也会触发 next 方法,因为预检请求并不包含 Authorization 字段,所以服务端会报错。

    想解决这个问题很简单,只需要在回调中过滤 option 方法即可

    res.statusCode = 204
    res.setHeader('Content-Length', '0')
    res.end()
    
    

    最⽅便的跨域⽅案Nginx

    nginx是⼀款极其强⼤的web服务器,其优点就是轻量级、启动快、⾼并发。

    现在的新项⽬中nginx⼏乎是⾸选,我们⽤node或者java开发的服务通常都需要经过nginx的反向代理。

    image.png

    反向代理的原理很简单,即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再讲请求转发给node或者java服务,这样就规避了同源策略。

    其它跨域⽅案

    1. HTML5 XMLHttpRequest 有⼀个API,postMessage()⽅法允许来⾃不同源的脚本采⽤异步⽅式进⾏有限的通信, 可以实现跨⽂本档、多窗⼝、跨域消息传递。
    2. WebSocket 是⼀种双向通信协议,在建⽴连接之后,WebSocket 的 server 与 client 都能主动向对⽅发送或接收数 据,连接建⽴好了之后 client 与 server 之间的双向通信就与 HTTP ⽆关了,因此可以跨域。
    3. window.name + iframe:window.name属性值在不同的⻚⾯(甚⾄不同域名)加载后依旧存在,并且可以⽀持⾮常 ⻓的 name 值,我们可以利⽤这个特点进⾏跨域。
    4. location.hash + iframe:a.html欲与c.html跨域相互通信,通过中间⻚b.html来实现。 三个⻚⾯,不同域之间利⽤ iframe的location.hash传值,相同域之间直接js访问来通信。
    5. document.domain + iframe: 该⽅式只能⽤于⼆级域名相同的情况下,⽐如 a.test.com 和 b.test.com 适⽤于该⽅ 式,我们只需要给⻚⾯添加 document.domain ='test.com' 表示⼆级域名都相同就可以实现跨域,两个⻚⾯都通过js 强制设置document.domain为基础主域,就实现了同域。
    2019-12-12 10:33:35
    赞同 1 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载