• 跨域问题


    跨域问题

    跨域

    出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。(不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。)

    浏览器中的跨域报错

    浏览器同源策略

    同源定义

    如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。(“元组” 是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)。

    例子: 和 http://oasis-f.com/index.html 的源进行对比。

    URL 是否同源 原因
    http://oasis-f.com/foo/bar.html 同源 只有路径不同
    https://oasis-f.com/foo/bar.html 协议不同
    http://oasis-f.com:8080/foo/bar.html 端口不同
    http://bad-oasis-f.com/foo/bar.html 主机不同

    不受同源限制的情况

    • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。
    • <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的 HTTP 头部 Content-Type 。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
    • 通过<img>展示的图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...
    • 通过<video> <audio> 播放的多媒体资源。
    • 通过 <object><embed> <applet> 嵌入的插件。
    • 通过 @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
    • 通过 <iframe> 载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互。

    JSONP

    利用 script 标签不受同源策略控制的特点来访问不同源下的资源。具体可以查看这篇文章,虽然年代比较早但是讲的很清楚。
    说说JSON和JSONP,也许你会豁然开朗,含jQuery用例

    不过使用 jsonp 也有一些额外的问题:

    • 只能发起 GET 请求
    • 因为浏览器的限制无法返回捕获到错误码
    • 安全性低

    CORS

    跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

    跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

    CORS请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

    简单请求

    不会触发 CORS 预检请求,后端程序只需要在返回的响应头中加上 Access-Control-Allow-Origin 字段,并且把该字段的值设置为 跨域请求的来源地址或简单的设置为 * 就可以了。
    简单请求需满足下列所述条件

    • 使用下列方法:
      • GET
      • HEAD
      • POST
    • 只设置下列 header:
      • 用户代理自动设置(Connection,User-Agent
      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type(只允许下列值)
        • text/plain
        • mujltipart/form-data
        • application/x-www-urlencoded
      • DPR
      • Downlink
      • Save-Data
      • Viewport-Width
      • Width
    • 请求中的任意 XMLHttpRequestUpload 对象没有注册任何事件监听器。
    • 请求中没有使用 ReadableStream 对象

    Node Express 例子

    // 客户端 静态服务运行于 http://192.168.0.119:8000
        function sendRequest(){
            fetch('http://192.168.0.119:3000')
            .then(res=>console.log(res)).catch(e=>console.error(e))
        }
    
    // 服务端
    const express = require('express');
    const app = express();
    const port = 3000;
    app.get('/',(req,res) => res.send('cors app'));
    app.listen(port,()=> console.log(`cors app listening on port ${port}`));
    

    此时请求跨域
    简单请求的跨域报错

    设置CORS相关请求头

    const express = require('express');
    const app = express();
    const port = 3000;
    
    // CORS 中间件
    const allowCrossDomain = (req,res,next)=>{
        res.header('Access-Control-Allow-Origin','http://192.168.0.119:8000');
        next()
    }
    app.use(allowCrossDomain)
    
    app.get('/',(req,res) => res.send('cors app'));
    app.listen(port,()=> console.log(`cors app listening on port ${port}`));
    
    

    这时候就可以成功请求到了。
    简单的跨域请求成功

    但是完整的应用不可能只有 GET 请求呀。需要有 更多的 method,cookie 等

       fetch('http://192.168.0.119:3000',{
                credentials: 'include',  // 发送带凭据的请求
                method: 'POST',
                body:JSON.stringify({foo:'bar'}),
                headers:new Headers({
                    'Content-Type':'application/json',
                    'Custom-Header':'8888'
                })
            })
            .then(res=>console.log(res)).catch(e=>console.error(e))
    
    ...
    const allowCrossDomain = (req,res,next)=>{
        res.header('Access-Control-Allow-Origin','http://192.168.0.119:8000');
        res.header('Access-Control-Allow-Methods','POST');
        res.header('Access-Control-Allow-Headers',"Content-Type,Custom-Header");
        res.header('Access-Control-Allow-Credentials','true');
        next()
    }
    ...
    

    这里有个需要注意的地方:

    对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“”。
    这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“
    ”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 http://foo.example,则请求将成功执行。
    另外,响应首部中也携带了 Set-Cookie 字段,尝试对 Cookie 进行修改。如果操作失败,将会抛出异常。

    参考链接

  • 相关阅读:
    CSS3盒模型display初探(display:box/display:flex)
    css伪元素研究(::before/::after)
    css后代选择器(div.class中间不带空格)
    css选择器(选择<div>内所有<p>元素)
    text-indent无效解决方案
    控制div位于最上层
    gulp用途
    Webpack打包工具实时更新操作(启用观察者模式)
    CLR/.NET/C#/Visual Studio/ASP.NET各版本之间的关系(转)
    前端打包/自动化构建工具:fis3
  • 原文地址:https://www.cnblogs.com/oasis-cuke/p/13399229.html
Copyright © 2020-2023  润新知