第72篇 跨域的简单介绍
1.跨域的相关概念
1.1 什么是跨域
同源策略是由 Netscape 提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源(origin)中加载文本或者脚本与来自其他源(origin)中资源的交互方式,所谓的同源就是指协议、域名、端口相同。
当浏览器执行一个脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域。
域名组成:
1.2 为什么会出现跨域
浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用 XMLHttpRequest 请求)。
1.3 浏览器的同源策略:Same-Origin Policy
- 没有同源策略的危害
防止恶行请求
案例讲解:
假设有一个黑客叫做小黑,他从网上抓取了一堆美女图做了一个网站,每日访问量爆表。
为了维护网站运行,小黑挂了一张收款码,觉得网站不错的可以适当资助一点,可是无奈伸手党太多,小黑的网站入不敷出。
于是他非常生气的在网页中写了一段js代码,使用ajax向淘宝发起登陆请求,因为很多数人都访问过淘宝,所以电脑中存有淘宝的cookie,不需要输入账号密码直接就自动登录了,然后小黑在ajax回调函数中解析了淘宝返回的数据,得到了很多人的隐私信息,转手一卖,小黑的网站终于盈利了。
如果跨域也可以发送AJAX请求的话,小黑就真的获取到了用户的隐私并成功获利了!!!
1.4 跨域有哪些限制
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM 无法获得。:钓鱼网站
- AJAX 请求不能发送。
1.5 Ajax请求步骤:
(1)创建XMLHttpRequest 对象。
(2)使用open方法设置请求的参数。open(method, url, 是否异步)。
(3)发送请求。
(4)注册事件。 注册onreadystatechange事件,状态改变时就会调用。
(5)获取返回的数据,更新UI。
2.JSONP(JSON with Padding)
2.1 概念
原理就是利用了 script 标签不受同源策略的限制,在页面中动态插入了 script,script 标签的 src 属性就是后端 api 接口的地址,并且以 get 的方式将前端回调处理函数名称告诉后端,后端在响应请求时会将回调返还,并且将数据以参数的形式传递回去。
2.2 核心代码
前端:
var script = document.createElement('script');
script.src = 'http://127.0.0.1:2333/jsonpHandler?callback=_callback';
document.body.appendChild(script);
var _callback = function(obj) {
for(key in obj) {
console.log('key: ' + key +' value: ' + obj[key]);
}
}
后端:
[HttpGet]
[Route("jsonpForVue")]
public void GetjsonpForVue(string callback)
{
TokenModelJwt tokenModelJwt=new TokenModelJwt(){
Role="admin",
Uid=1,
Work="dsdf"
};
//string call = "({" + response + "})";
string response = string.Format("\"name\":\"{0}\"", "zhagnsan");
var modlestr = JsonConvert.SerializeObject(tokenModelJwt);
string call = callback + "(" + modlestr + ")";
Response.WriteAsync(call);
}
2.3 优劣
劣:
- 这种方式只能发生get请求;
- 确定jsonp的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定;
- 不太安全,可能也会受到攻击;
优:
- 很简单
- 老式浏览器全部支持,服务器改造非常小
3 Proxy代理
3.1 概念
- vue-cli的proxyTable用的是http-proxy-middleware中间件
- create-react-app用的是webpack-dev-server内部也是用的http-proxy-middleware
- http-proxy-middleware内部用的http-proxy
跨域是浏览器禁止的,服务端并不禁止跨域
所以浏览器可以发给自己的服务端然后,由自己的服务端再转发给要跨域的服务端,做一层代理。
3.2 核心代码
前端:
proxy: {
// 配置多个代理
"/api": {
target: "http://localhost:5000",//这里改成你自己的后端api端口地址,记得每次修改,都需要重新build
//target: "http://localhost:58427",
//target: "http://api.douban.com",
ws: true,
changeOrigin: true,
pathRewrite: {
// 路径重写,
"^/apb": "" // 替换target中的请求地址
}
}
3.3 优劣
缺点:不能用在生产环境,只能在开发环境;
优点:dev环境配置很简单,支持多个域名;
4 CORS,全称:Cross-Origin Resource Sharing(跨域资源共享)
4.1 概念
CORS是一种允许当前域(origin)的资源被其他域(origin)的脚本请求访问的机制。
当使用 XMLHttpRequest 发送请求时,浏览器如果发现违反了同源策略就会自动加上一个请求头 origin,后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin,值就是发起请求的源地址(http://127.0.0.1:8888),浏览器得到响应会进行判断 Access-Control-Allow-Origin 的值是否和当前的地址相同,只有匹配成功后才进行响应处理。
简单请求
- GET
- HEAD
- POST
- 条件 2:Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
复杂请求
4.2 核心代码
后端配置
4.3 优劣
缺点:
- 部分老的浏览器不支持
优点:
- 支持所有 Http 谓词请求
- 不用注意接口规则
- 可用在生产环境
5 Nginx
概念:nginx解决跨域 是利用其反向代理的能力
5.1 跨域原理
Brower =》 host =》 nginx =》 目标地址服务器数据 =》 nginx =》 Brower
也就是说,nginx并不是通过监听brower的请求。而是作为一个服务器,接收外部对本机的请求。所以是先通过host,让请求指向本机,才会经过nginx。才能进行转发。
- 首先,直接在浏览器地址栏中,输入某接口地址。是不会产生跨域问题的。
- 只有当在某域名的页面中,由该页面发起的接口请求。才可能会跨域。
- nginx就类似于这个浏览器地址栏,它接收到外部对它的请求( 注意,nginx只会接收别人对它的请求,而不会拦截浏览器的请求 ),再类似浏览器地址栏一样去请求某个接口。最后将请求到的内容返回回去
5.2 核心代码
server {
listen 1005;
server_name localhost;
location / {
root nuxt\Blog.Admin\dist;
index index.html index.htm;
}
location /api {
rewrite ^.+apb/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass http://localhost:1004; #// 这是 netcore 端口
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
5.3 优劣
缺点:
- 移植灵活性 低、每套环境配置可能均不相同
优点:
- 对独立性强的小项目,使用nginx则可以降低你的开发成本,快速发开快速上线
6 Socket
WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
核心代码:
var ws = new WebSocket('wss://echo.websocket.org'); //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。
//把请求发出去
ws.onopen = function (evt) {
console.log('Connection open ...');
ws.send('Hello WebSockets!');
};
//对方发消息过来时,我接收
ws.onmessage = function (evt) {
console.log('Received Message: ', evt.data);
ws.close();
};
//关闭连接
ws.onclose = function (evt) {
console.log('Connection closed.');
};