【Nodejs】跨域

news2024/11/25 15:30:28

在这里插入图片描述

1.什么是跨域


跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议任一不同,都属于跨域。

同源策略限制了一下行为:

Cookie、LocalStorage 和 IndexDB 无法读取

DOM 和 JS 对象无法获取

Ajax请求发送不出去

下面举个例子:

http://www.yyy.cn/index.html 调用 http://www.xxxyyy.cn/server.php 非跨域
http://**www.xxxyyy.cn**/index.html 调用  http://**www.xxx.cn**/server.php  跨域,主域不同
http://**abc**.xxxyyy.cn/index.html 调用  http://**def**.xxx.cn/server.php  跨域,子域名不同
http://www.xxx.cn:**8080**/index.html 调用  http://www.xxx.cn/server.php  跨域,端口不同
**https**://www.xxx.cn/index.html 调用  **http**://www.xxx.cn/server.php  跨域,协议不同

2.跨域资源共享CORS


这是目前主流的解决方案。

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。IE8+:IE8/9需要使用XDomainRequest对象来支持CORS

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。

基本只需要对服务器动手脚,前端代码跟同源时一样,也就是不跨域时一样。

这种方式分为两种请求:
一种是简单请求,另一种是非简单请求。只要满足下面条件就是简单请求,请求方式为HEAD、POST 或者 GET,http头信息不超出以下字段:

Accept、Accept-Language 
Content-Language
Last-Event-ID
Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
为什么要分为简单请求和非简单请求,因为浏览器对这两种请求方式的处理方式是不同的。

2.1 简单请求

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...

Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。 浏览器发现,这个响应头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。

注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:

  Access-Control-Allow-Origin: http://api.bob.com
  Access-Control-Allow-Credentials: true
  Access-Control-Expose-Headers: FooBar
  Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头

**Access-Control-Allow-Origin *:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个,表示接受任意域名的请求

Access-Control-Allow-Credentials: 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

withCredentials 属性

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

另一方面,开发者必须在AJAX请求中打开withCredentials属性。

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};
//  jquery
$.ajax({
        ...
       xhrFields: {
             withCredentials: true    // 前端设置是否带cookie
       },
       crossDomain: true,  // 会让请求头中包含跨域的额外信息,但不会含cookie
        ...
});

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。 但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

*需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。**同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie

2.2 非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)——浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。

  OPTIONS /cors HTTP/1.1
  Origin: http://api.bob.com
  Access-Control-Request-Method: PUT
  Access-Control-Request-Headers: X-Custom-Header
  Host: api.alice.com
  Accept-Language: en-US
  Connection: keep-alive
  User-Agent: Mozilla/5.0...

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求头信息包括两个特殊字段。

Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。

Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

预检请求的回应

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。

如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出报错信息。

服务器回应的其他CORS相关字段如下:

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
  • Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
  • Access-Control-Allow-Headers:如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
  • Access-Control-Allow-Credentials: 该字段与简单请求时的含义相同。
  • Access-Control-Max-Age: 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

浏览器正常请求回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

浏览器的正常CORS请求。上面头信息的Origin字段是浏览器自动添加的。下面是服务器正常的回应。

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin字段是每次回应都必定包含的

3.Nodejs 跨域处理


3.1 设置允许域名跨域

  • 设置允许所有域名跨域
var express = require('express');
var app = express();
 
// 设置允许所有域名跨域:
app.all("*",function(req,res,next){
    //设置允许跨域的域名,*代表允许任意域名跨域
    res.header("Access-Control-Allow-Origin","*");
    //允许的header类型
    res.header("Access-Control-Allow-Headers","content-type");
    //跨域允许的请求方式 
    res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
    if (req.method.toLowerCase() == 'options')
        res.send(200);  //让options尝试请求快速结束
    else
        next();
})
  • 设置允许多个域名跨域:
app.all("*",function(req,res,next){
  var orginList=[
      "http://www.bibi.com",
      "http://www.qq.com",
      "http://www.baidu.com"
  ]
  // 防止undefined 报错
  if(!req.headers.origin){
    return
  }
   if(orginList.includes(req.headers.origin.toLowerCase())){
    //允许的header类型
    res.header("Access-Control-Allow-Headers", "content-type");
    //跨域允许的请求方式
    res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
        //设置允许跨域的域名,*代表允许任意域名跨域
        res.header("Access-Control-Allow-Origin",req.headers.origin);
        if (req.method.toLowerCase() == 'options'){
          res.sendStatus(200);  //让options尝试请求快速结束
        }
        else{
          next();
        }
    } else {
      res.sendStatus(500)
    }
})

3.2 利用中间件 cors

安装 npm i cors -S

使用方式一 : 为所有源启用跨域

const  cors = require('cors')
 app.use(cors())
 app.listen(8000, function () {
   console.log('start')
 })

使用方式二 : 按条件配置

 var express = require('express')
 var cors = require('cors')
 var app = express()
 var whitelist = ['http://example1.com', 'http://example2.com']
//  异步配置
  var corsOptions;
 var corsOptionsDelegate = function (req, callback) {
  if (whitelist.indexOf(req.header('Origin')) !== -1) {
    corsOptions = { origin: true} //在CORS响应中反映(启用)请求的起源
  } else {
     corsOptions = { origin: false} // 拦截请求
  }
  callback(null, corsOptions) //  error  options
}

 app.all("*", cors(corsOptionsDelegate), function (req, res, next) {
 if(corsOptions.origin  === true){
    if (req.method.toLowerCase() == 'options'){
      res.sendStatus(200);  //让options尝试请求快速结束
     }
     else{
      next();
     }
   } else {
    res.sendStatus(500); //被拦截
   }
 })
  app.post('/cors',(req,res)=> {
  res.send('ok')
})
 app.listen(8000, function () {
   console.log('start')
 })

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/794649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud【为什么需要监控系统、Prometheus环境搭建、Grafana环境搭建 、微服务应用接入监控 】(十七)

目录 全方位的监控告警系统_为什么需要监控系统 全方位的监控告警系统_Prometheus环境搭建 全方位的监控告警系统_Grafana环境搭建 全方位的监控告警系统_微服务应用接入监控 全方位的监控告警系统_为什么需要监控系统 前言 一个服务上线了后,你想知道这个服…

PostgreSQL-Character with value 0x09 must be escaped.

在使用json相关函数时,报了这个错: Character with value 0x09 must be escaped.中文即使:值为0x09的字符必须转义。 找了下这个0x09 这个ASCII的值,是水平制表符。那这应该是因为json不支持换行导致的,我们将水平制…

spring-websocket在SpringBoot(包含SpringSecurity)项目中的导入

✅作者简介:大家好,我是 Meteors., 向往着更加简洁高效的代码写法与编程方式,持续分享Java技术内容。 🍎个人主页:Meteors.的博客 🥭本文内容:spring-websocket在SpringBoot(包含SpringSecurity…

移动云携手启明星辰打造云网全域安全能力 提供全方位网络安全防护和风险控制服务

为满足大众安全用云、智慧用云的需求,中国移动云能力中心与启明星辰强强联合打造移动云|星辰安全—云网全域安全能力。此次合作深度融合信息技术与业务运营,提供全方位的网络安全防护和风险控制服务,满足国家合规要求和大众用云需求&#xff…

从小白到大神之路之学习运维第68天-------Nginx企业级优化与防盗链

第三阶段基础 时 间:2023年7月26日 参加人:全班人员 内 容: Nginx企业级优化与防盗链 目录 一、配置Nginx隐藏版本号 二、修改Nginx用户与组 ​三、配置Nginx网页缓存时间 四、实现Nginx的日志切割 五、配置Nginx实现连接超时 六…

图为科技T501赋能工业机器人 革新传统工业流程

工业机器人已成为一个国家制造技术与科技水平的重要衡量标准,在2019年,中国工业机器人的组装量与产量均位居了全球首位。 当前,工业机器人被广泛用于电子、物流、化工等多个领域之中,是一种通过电子科技和机械关节制作出来的智能机…

让婚礼策划展示小程序成为你的必备利器

在当今互联网时代,微信小程序已经成为了很多企业和个人展示自己产品和服务的重要渠道。如果你想学习微信小程序开发,下面将为你介绍一些基本步骤。 首先,你需要注册并登录一个第三方小程序制作平台,比如乔拓云平台。这些平台提供了…

uiautomatorViewer无法获取Android8.0手机屏幕截图的解决方案

问题描述: 做APP UI自动化的时候,会碰到用uiautomatorViewer在Android 8.0及以上版本的手机上,无法获取到手机屏幕截图,无法获取元素定位信息的问题,会有以下的报 在低版本的Android手机上,则没有这个问题…

探索:Modbus TCP协议(应用层)

目录 一,Modbus起源 ModbusTCP协议格式 1,报文头 2,寄存器 3,功能码 练习: 工具软件使用 三方库的使用 【1】库的安装 1.库的安装配置 2.库的使用 【2】函数接口 【3】编程流程 代码实战 一,Modbus起源 起源: Modbus由Modicon公司…

【数据分享】1901-2022年1km分辨率逐月平均气温栅格数据(全国/分省/免费获取)

气温数据是我们最常用的气象指标之一,之前我们给大家分享过1950-2022年0.1 x 0.1精度的逐月平均气温栅格数据和逐年平均气温栅格数据(均可查看之前的文章获悉详情)! 本次我们分享的是精度更高的气温栅格数据——1901-2022年1km分…

【数据分享】全国地级市1999—2020年工业企业数(Shp/Excel格式)

在之前的文章中,我们分享过基于2000-2022年《中国城市统计年鉴》整理的1999-2021年地级市的人口相关数据、各类用地面积数据、污染物排放和环境治理相关数据、房地产投资情况和商品房销售面积、社会消费品零售总额和年末金融机构存贷款余额(可查看之前的…

[Golang] Viper原理以及详细使用案例

文章目录 什么是 Viper?基础配置引入依赖:动态监听原理分析:监听原理分析 Config.yaml文件配置Viper文件配置 什么是 Viper? 介绍:用于处理配置文件中解析和读取配置文件 优点:支持多种配置格式&#xff0…

办公软件巨头CCED、WPS迎来新挑战,新款办公软件已形成普及之势

办公软件巨头CCED、WPS的成长经历 众所周知,CCED和WPS是中国办公软件行业的两大知名品牌。 但它们的成长经历不是一蹴而就的,都是经历了漫长的发展过程的。 CCED是中国大陆早期的一款文本编辑器,它在上个世纪80年代末和90年代初非常流行。 …

Flutter Widget Life Cycle 组件生命周期

Flutter Widget Life Cycle 组件生命周期 视频 前言 了解 widget 生命周期,对我们开发组件还是很重要的。 今天会把无状态、有状态组件的几个生命周期函数一起过下。 原文 https://ducafecat.com/blog/flutter-widget-life-cycle 参考 https://api.flutter.dev/f…

低代码和零代码有哪些区别?

低代码开发的概念 低代码开发是一种新兴的软件开发方法,其核心是通过使用图形用户界面和可视化建模工具,来减少编写代码的工作量和技能要求。低代码开发平台通常提供了丰富的预定义组件和模板,可以帮助开发人员快速构建应用程序。开发人员只…

【雕爷学编程】Arduino动手做(173)---SG90舵机双轴云台模块

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

新思的DW ECC计算模块

参考文件:synopsys学习资料-dw_ecc.pdf 设计的模块:

一些需要用到的网址

https://npm.taobao.org/mirrors/chromedriver/ (谷歌浏览器镜像网址) 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。…

Neo4j 集群和负载均衡

Neo4j 集群和负载均衡 Neo4j是当前最流行的开源图DB。刚好读到了Neo4j的集群和负载均衡策略,记录一下。 1 集群 Neo4j 集群使用主从复制实现高可用性和水平读扩展。 1.1 复制 集群的写入都通过主节点协调完成的,数据先写入主机,再同步到…