跨域-反向代理-正向代理
同源策略:同源-协议、域名和端口相同
同源策略(Same-Origin Policy):同源策略是浏览器的一项安全特性,它要求网页中的JavaScript只能请求同源(即协议、域名、端口均相同)的资源。这是因为不同域之间的资源请求可能涉及潜在的安全风险,如跨站点请求伪造(CSRF)和跨站脚本攻击(XSS)。CORS允许服务器明确地指定哪些跨域请求是安全的,以突破同源策略的限制。
非同源:
(1)同一域名,不同端口
http://www.demo.com:8000/a.js
http://www.demo.com/b.js
(2)同一域名,不同协议
http://www.demo.com/a.js
https://www.demo.com/b.js
(3)域名和域名对应相同ip
http://www.demo.com/a.js
http://127.0.0.1/b.js
(4)不同域名
http://www.demo1.com/a.js
http://www.demo2.com/b.js
Cross-Origin Resource Sharing
CORS允许服务器明确地指定哪些跨域请求是安全的,以突破同源策略的限制
资源访问控制:CORS允许服务器控制哪些网站可以访问其资源。服务器可以配置CORS策略,以明确指定允许的来源,以防止资源被不信任的网站滥用。
减少恶意行为:如果不限制跨域资源访问,恶意网站可能会利用跨域请求来窃取用户信息或进行其他不当行为。CORS可以减少这种风险。
虽然CORS是一项有助于安全的机制,但它也可以通过服务器的配置来允许跨域请求,以确保合法的跨域通信。这使得开发者可以在需要时安全地让不同域之间的Web应用程序进行通信,同时维护数据隐私和网络安全。
如何配置
以下是一些常见的方法来配置CORS,具体取决于你使用的服务器端技术:
- 使用HTTP头信息:CORS配置的主要方式是通过在HTTP响应头中设置特定的CORS头信息。以下是一些常见的CORS头信息:
- Access-Control-Allow-Origin:指定允许跨域访问的域名。可以是单一域名、多个域名,或使用通配符*表示允许任何域访问。
- Access-Control-Allow-Methods:指定允许的HTTP方法,如GET、POST、PUT、DELETE等。
- Access-Control-Allow-Headers:指定允许的HTTP请求头,如Content-Type、Authorization等。
- Access-Control-Expose-Headers:指定浏览器可以访问的响应头。
- Access-Control-Allow-Credentials:指定是否允许跨域请求发送凭证信息,如Cookie或HTTP认证。
- 服务器配置:具体的CORS配置方法取决于你使用的服务器技术。以下是一些服务器配置示例:
- Node.js(Express):使用cors中间件,可以在Express应用中配置CORS。
- Apache:使用.htaccess文件来配置CORS。
- Nginx:在Nginx配置文件中添加CORS设置。
- 预检请求(Preflight Request):对于某些复杂的跨域请求(例如带有自定义请求头的请求),浏览器会首先发送一个预检请求(OPTIONS请求),以获取服务器是否允许实际请求。你需要处理这个预检请求,并设置相应的CORS头信息。
其他跨域方式总结
JSONP:JSONP(JSON with Padding)是一种跨域解决方案,它利用了script标签没有跨域限制的特性。通过在请求URL中传递一个回调函数名,服务器将数据包装在回调函数中返回给客户端,从而实现跨域请求。缺点是只支持GET请求。
反向代理:服务器反向代理,将客户端请求转发到目标服务器,并将响应返回给客户端,从而实现跨域请求。需要额外的服务器资源
postMessage:可以使用HTML5提供的postMessage方法,在不同域之间进行消息传递,从而实现跨域通信。
方法2-反向代理实现跨域
原理:跨域是浏览器禁止的,服务端并不禁止跨域
Proxy(代理):用户先将数据发给代理服务器,再由代理服务器转发给目的服务器,又分为正向代理和反向代理:
- 反向代理中,客户端不知道真正的服务器是谁,以为自己访问的就是真实的服务器。
- 正向代理中,服务器不知道真正的客户端到底是谁,以为访问自己的就是真实的客户端。

反向代理:反向代理是服务器架设的,反向代理确保用户不会与后端服务器直接通信
客服端无法知道的服务器地址,因为真实的服务器被代理了,客服端请求的只是代理的地址;其实是"代理服务器"代理了"目标服务器",去和"客户端"进行交互。
客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端;
此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。
跨域中反向代理
- 1.启动一个node服务,这个服务的作用1是静态文件服务,让你可以访问到html/js等文件包括监听文件变动等, 2.是启动一个http代理,你js发送的请求会请求到这个服务A,由服务A代理到服务B,而服务A和静态文件服务器是同源的,并不影响同源策略。
- 2.浏览器是没有必要设置CORS的,服务器设置CORS就是为了告知浏览器允许访问我的源,不是跟我同源的,要在浏览器接受到响应后抛出错误。
举个简单的例子: 前端请求 服务器a,然后就得到了想要的数据。但其实 服务器a 中本身没有部署任何接口,它只是偷偷地从 服务器b 上取来数据,返回给前端。 这个过程只在server端发生,前端是无感知的。 https://baijiahao.baidu.com/s?id=1709750103413296714&wfr=spider&for=pc 实例:vue-cli生成的配置文件上的proxytabl
dev: {
env: require('./dev'),
port: 9000,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
}
}
}
nginx 的反向代理
那么如何只需要输入 www.123.com 便可以跳转到 Tomcat初始界面呢?便用到 nginx的反向代理。
server {
listen 80;
server_name www.123.com;
location / {
proxy_pass http://127.0.0.1:8080;
index index.html index.htm index.jsp;
}
}
正向代理:需要在客户端进行一些代理的设置,隐藏真实客户端地址
"代理服务器"代理了"客户端",去和"目标服务器"进行交互。 正向代理一般是客户端架设的,比如在自己的机器上安装一个代理软件。帮助客户端访问其无法访问的服务器资源。 反向代理则是服务器的代理,帮助服务器做负载均衡,安全防护等。
正向代理作用:
反向代理的作用主要是为服务器做缓存和负载均衡: 反向代理的负载均衡是指:在多个真正的服务器前架设一个代理服务器,用户所有的数据都发给代理服务器,然后代理服务器根据各个真实服务器的状态将数据转发给一个任务较少的服务器处理。
隐藏服务器真实IP: 使用反向代理,可以对客户端隐藏服务器的IP地址。
shadowsocks 等协议 翻墙所用的代理都是正向代理。vpn常用,比如你通过vpn的服务器去访问google,此时vpn的服务器就是客户端
跨域实现-jsonp和img
使用jsonp:允许 script 加载第三方资源
它只支持GET请求而不支持POST等其它类型的HTTP请求,JSONP有两部分组成:
- 回调函数。回调函数的名字一般是在请求中指定。
- 和数据就是传入回调函数中的json数据。
JSONP的工作原理是通过在页面中插入<script>标签来获取数据,由于<script>标签不受同源策略的限制,因此可以从不同域名获取数据。
<!DOCTYPE html>
<html>
<head>
<title>JSONP Example</title>
</head>
<body>
<script>
// 步骤1: 创建回调函数
function handleData(data) {
console.log("Data received:", data);
}
// 步骤2: 创建<script>标签来获取数据
const script = document.createElement('script');
script.src = 'https://example.com/api/data?callback=handleData';
// 步骤3和步骤4:浏览器执行响应中的JavaScript函数
document.body.appendChild(script);
</script>
</body>
</html>
在上面的示例中,handleData函数是在步骤1中创建的回调函数,<script>标签的src属性包含一个callback参数,其值是回调函数的名称。当<script>标签加载并执行目标服务器的响应时,会调用handleData函数来处理数据。
在Node.js中,你需要处理JSONP请求,并根据请求的callback参数将数据包装为JavaScript函数的调用。
const express = require('express');
const app = express();
// 处理JSONP请求
app.get('/api/data', (req, res) => {
const data = { message: 'Hello, JSONP!' };
// 获取回调函数名称
const callback = req.query.callback;
// 如果提供了回调函数名称,包装数据并返回
if (callback) {
res.jsonp({ data });
} else {
// 如果没有提供回调函数名称,直接返回数据
res.json({ data });
}
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
需要注意的是,JSONP存在一些安全风险,因为它要求信任目标服务器,且不提供数据的完整性验证。因此,只应该在信任的源上使用JSONP,或者在其他安全措施的支持下使用。另外,现代Web开发通常更倾向于使用CORS来进行跨域请求,因为它提供了更多的安全性和控制。
图片跨域只能用于浏览器与服务器的单向通信-->用的比较少
let img = new Image()
img.onload = img.onerror = function(){
console.log('done')
}
img.src = 'http://www.xx.com/test?name=ruo'
/*
创建Image,然后将onload和 onerror事件处理程序指定为同一个函数。无论什么样的响应,只要请求完成,就能得到通知。
缺点:1.只能发送get 2.无法访问服务器的响应文本。
只能用于浏览器与服务器的单向通信
*/
跨域-预请求
AJAX 跨域请求可以分为两种:简单请求与非简单请求
- 简单请求:
请求方法为 HEAD、GET、POST 中的 1 种
请求的 header 中没有自定义的请求头
Content-Type 为以下几种:application/x-www-form-urlencoded、multipart/form-data、text/plain等
- 非简单请求:
header 中包含自定义请求头的 AJAX 请求
PUT、DELETE 形式的 AJAX 请求
Content-Type 字段的类型是 application/json 等
预检请求定义/触发条件
当发起跨域请求时,简单请求只发起一次请求;复杂请求则需要2次,先发起options请求,确认目标资源是否支持跨域,浏览器会根据服务端响应的header自动处理剩余的请求,如果响应支持跨域,则继续发出正常请求;不支持的话,会在控制台显示错误。
预检请求是预先检查服务器是否允许发起实际的请求,避免跨域请求对服务器的用户数据产生未预期的影响,让跨域更加的安全。
总结触发option条件:
- 前提是发生跨域请求
- 触发一定条件,例如post请求的Request headers 的 content-type为application/json
注意:
1. 必须是request header;
2. get请求设置不了content-type,因为get会把参数拼接在url上
所以,当触发预检时,跨域请求便会发送2次请求,增加请求次数,同时,也延迟了请求真正发起的时间,会影响性能。
在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证 相关数据)。
需要注意的是,Option请求是由浏览器发起的,用于查询服务端是否支持跨域访问。在客户端无法控制服务端的情况下,无法完全避免Option请求。
使用HTTP GET或POST请求替代Option请求。如果你需要跨域访问资源,可以使用JSONP或者CORS(跨域资源共享)来进行跨域请求。
使用代理服务器。如果你需要访问其他域的资源,可以使用代理服务器来进行访问,代理服务器将请求转发到目标服务器,这样就不需要跨域访问了。
将目标资源放到同一域名下。如果你有控制目标资源的权利,可以将目标资源放到与你的网站同一域名下,这样就不需要跨域访问了。
优化options请求的3种方法:
- 对 options 请求进行缓存。服务器端设置 Access-Control-Max-Age 字段
服务器端设置 Access-Control-Max-Age 字段,那么当第一次请求该 URL 时会发出 OPTIONS 请求,浏览器会根据返回的 Access-Control-Max-Age 字段缓存该请求的 OPTIONS 预检请求的响应结果(具体缓存时间还取决于浏览器的支持的默认最大值,取两者最小值,一般为 10 分钟)。在缓存有效期内,该资源的请求(URL 和 header 字段都相同的情况下)不会再触发预检。(chrome 打开控制台可以看到,当服务器响应 Access-Control-Max-Age 时只有第一次请求会有预检,后面不会了。注意要开启缓存,去掉 disable cache 勾选。)
Access-Control-Max-Age这个响应首部表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存的最长时间,单位是秒。(MDN)
- withCredentials为true时需要后端配置响应头Access-Control-Allow-Credentials为true (不会产生预请求)
前端:
let xhr = new XMLHttpRequest()
xhr.open('POST','http://127.0.0.1:8080/Public//testPost')
xhr.withCredentials = true
xhr.onload = function(res){
console.log(res)
}
xhr.send()
当请求跨域,后端没有配置时,报错如下
则后端配置代码:
public function testPost(){
header("Access-Control-Allow-Origin: *");
echoJsonResult(1,'',$_POST);
}
- 用其它的跨域方式做跨域请求
关于缓存:预检请求不一定每一次都会产生
- 这个因为浏览器会对预检请求进行缓存,同时通过服务器端设置 Access-Control-Max-Age 字段来设置缓存时间 那么当第一次请求该 URL 时会发出 OPTIONS 请求,浏览器会根据返回的 Access-Control-Max-Age 字段缓存该请求的 OPTIONS 预检请求的响应结果(具体缓存时间还取决于浏览器的支持的默认最大值,取两者最小值,一般为 10 分钟)。在缓存有效期内,该资源的请求(URL 和 header 字段都相同的情况下)不会再触发预检。(chrome 打开控制台可以看到,当服务器响应 Access-Control-Max-Age 时只有第一次请求会有预检,后面不会了。注意要开启缓存,去掉 disable cache 勾选。)
在 Firefox 中,上限是24小时 (即 86400 秒)。
在 Chromium v76 之前, 上限是 10 分钟(即 600 秒)。
从 Chromium v76 开始,上限是 2 小时(即 7200 秒)。
Chromium 同时规定了一个默认值 5 秒。
// 如果值为 -1,表示禁用缓存,则每次请求前都需要使用 OPTIONS 预检请求。
es.setHeader('Access-Control-Max-Age',600) //10秒