【网络基础】初级前端需要掌握知识,什么是同源策略与跨域,如何解决跨域问题

同源策略

同源策略是一种web应用的安全策略,规定了在浏览器中如果双方的协议、域名、端口号任意一个不相同,那么一方的脚本就不能访问另一方的数据。

js文件的获取,css文件的获取,图片访问是不会有同源策略限制的。注意这个同源的限制是浏览器做的,如果你用postman去请求则不会有这个问题

解决场景

同源策略被定义出来是有原因的,举一些没有同源策略的场景会发生什么:

例如当一个用户登录了A网站时,被注入了恶意js代码后,可能向其他域名的服务器发送请求,发送的数据就是A网站当前的用户数据,或者模拟用户的购买行为等。

再例如一个网站可以随意的把优酷、腾讯视频、b站的视频资源请求到,那么这个网站就可以做个集合所有平台的网站了。

分辨同源

怎么分辨两个URL是否同源,刚刚也说了协议、域名、端口号要一致,实际上这三个分辨的场景类型蛮多的,我觉得可以先了解一下最常规的类型就可以了,真正用到的时候再去查找积累即可。

例如

http://www.pangzong.com:8080/dir/index.html

只要另一个地址协议为http、域名为www.pangzong.com、端口号为8080,那么就可以认为双方是同源的。例如:

http://www.pangzong.com:8080/dir1/common/shopping.html


跨域

跨域就是双方不是同源的情况下,仍需要请求非同源资源出现的禁止请求问题。

跨域请求其实是非常常见的,比如一些网站下面有很多子域,同是一家人还不能访问就很离谱。又或者是浏览器正常向服务端发送请求,请求的地址和网站地址不是同域的,也拿不到数据。

注意了!当跨域请求发出去后,是正常发出的,服务器也会正常响应返回数据,只不过被浏览器拦截校验了而已。

非跨域资源

前面提到了,js文件,css文件,图片访问是不会有同源策略限制的,所以:

  1. <img />可以用于统计打点,用于第三方统计服务
  2. js文件可以用CDN请求获取跨域资源

跨域限制初心是好的,但是对页面的开发带来了一些不便,于是出现了很多解决跨域问题的方案,下面列举几个。

跨域解决方案

CORS标准

这是个W3C标准,叫跨域资源共享,允许浏览器跨域发送XML跨域请求。

当浏览器和服务器同时支持CORS,就可以使得双方进行跨域交流。

想具体了解看阮一峰老师的这篇文章

这里我就简单讲下,首先前端要满足一些条件:

  • 请求方法要为get、post、head
  • 请求头要符合CORS标准规范(例如Content-Type要为规范里规定的值)

然后服务器要满足一些条件:

发送请求后,服务器在收到,判断请求头里的Origin属性,是否为网站的地址,然后返回的数据响应头要有Access-Control-Allow-Origin: http:xxxx告知浏览器,不要对这个返回的数据做跨域校验了,他是从你这里发出的。

如果自己在请求头上加了自定义的东西或者改了Content-Type的值,会先发个option请求叫预检请求,让服务端确认下我接下来的请求是否能正常请求,然后服务端返回检验结果,大概过程是这样:
在这里插入图片描述

图片来源渡一教育

完成预检后,这个接口就可以继续按照刚刚所说的那样发起了。

JSONP协议

很老旧的方案了,在没有CORS方案的时代,就使用这个。

前面我们提到,在JS中引入一个外界的JS文件(可以通过script标签的url属性去获取),不会有跨域的限制。我们就可以利用这点解决跨域问题。

场景举例:

浏览器想向服务器获取一个用户的用户名,首先js文件中会定义一个函数,这个函数的功能就是获取到用户名称后展示出来:

function showName(data) {
	console.log(data)
}

然后js文件中还有一个函数能够动态生成script标签,且地址为服务端地址+用户id+showName函数名称,例如(可以在本地环境做测试):

<script src="http://localhost:8002/jsonp.js?userId=1&callback=showName"></script> 

在服务端中,会通过后端代码的操作,定义了同名的函数并且放入需要的数据,例如:

showName({
	userName: '小明'
})

那么,最终在浏览器中,我们看到的完整js代码为:

<script>
function showName(data) {
	console.log(data)
}
</script> 
<script src="http://localhost:8002/jsonp.js?userId=1&callback=showName"></script> 

等同:

<script>
function showName(data) {
	console.log(data)
}
showName({
	userName: '小明'
})
</script> 

完美的拿到后端的数据!

同理通过定义不同的回调和拼接属性url就可以获取到不同的数据。然后我们在来看看JSONP的定义就能读懂了:

JSONP缺点:它不是ajax请求,所以无法发送POST请求。

Iframe内嵌跨域通信

如果是内嵌在一个非同域名的网站中,可以使用window.postMessage()的这个html5新特性去跨域传递数据。

父层:

sendMes(){ // 向iframe发送
	let iframdom = this.$refs.iframdom // 拿到iframe的dom
	let _window = iframdom.contentWindow // 拿到iframe的window对象
	let _document = iframdom.contentDocument // 拿到iframe的document对象
	_window.postMessage({type: 'sendMes', text: '内容'}, '*') 
	// 注意点:1 第一个参数传内容用对象装,因为webpack会默认主动触发一次,
	// 即使sendMes没执行,这样被第一次触发的时候我们能通过对象的内容去区分。
	// 2 第二个参数写传入的地址是什么,如果不限制地址写*即可
}

window.addEventListener("message", (e) => { // 接收子层
	// e.data及postMessage的第一个参数,e.origin及postMessage的第二个参数
})

子层:

window.addEventListener("message", (e) => { // 接收父层
	// e.data及postMessage的第一个参数,e.origin及postMessage的第二个参数
})

sendOut(){ // 向父层发送
	window.top.postMessage({type: 'sendMes', text: '内容'}, '*') 
}

这种方法个人认为有两个不好的地方:

  • 当传输的内容多了,整个工程到处都是postMessage,到处监听,会非常的混乱。建议监听统一放到类似App.vue的组文件中。
  • 父层与子层需要定好规则,例如你的项目是子层,就要去问负责开发父层网站的开发人员,怎么按照他们的规范去传递数据和获取回数据

这里推荐篇基础使用文章:【记录postMessage的详细使用
再推荐篇补充的:【终于搞懂了 Iframe (跨窗口通信)

代理

起一个代理服务器,例如框架里的proxy配置,利用本地服务器做代理,可看这篇react的文章

上生产的话,可以通过nginx的配置实现代理

server {
    listen    80;
    # server_name www.josephxia.com;
    location / {
        root  /var/www/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    location /api {
        proxy_pass  http://127.0.0.1:3000;
        proxy_redirect   off;
        proxy_set_header  Host       $host;
        proxy_set_header  X-Real-IP     $remote_addr;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

原理就是让Nginx帮浏览器做个反向代理,大致过程就是浏览器发送请求发给的是Nginx服务器,后者再去和真正的服务器发送请求(服务端之间是没有跨域限制的),接收到数据后,再以CORS的方式放回给浏览器。

在本地的话dev-server请求对象一般也是Nginx服务器。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值