前端跨域大全

前端跨域大全

在这篇文章中,你会了解到

  • 浏览器为什么要跨域
  • 常见的几种跨域方法以及其对应的优势和弊端
  • 几种常见跨域方式的代码实现

一、什么是跨域以及为什么要限制跨域

(1)什么是跨域

  • 浏览器同域策略:协议、域名、 端口,都一样

http://store.company.com/dir2/other.html 同源 只有路径不同
http://store.company.com/dir/inner/another.html 同源 只有路径不同
https://store.company.com/secure.html 失败 协议不同
http://store.company.com:81/dir/etc.html 失败 端口不同 ( http:// 默认端口是80)
http://news.company.com/dir/other.html 失败 主机不同

因此只要有一种不一样,即算作跨域请求。

(2)为什么要限制跨域这里我就真的不罗嗦了,大家看下面那篇文章即可,已经讲的很好啦
目前,如果非同源,共有三种行为受到限制。

  1. 无法获取非同源网页的 cookie、localstorage 和 indexedDB。
  2. 无法访问非同源网页的 DOM (iframe)。
  3. 无法向非同源地址发送 AJAX 请求 或 fetch 请求(可以发送,但浏览器拒绝接受响应)。

对于三种行为的危害更生动的描述,可以看这篇文章
不要再问我跨域的问题了

二、有哪些常见的跨域方式?

jsonp
cors
postMessage
document.domain
window.name
location.hash
图像Ping
http-proxy(例如webpack反向代理)
nginx
Comet(服务器推送技术)
websocket

1.jsonp

(1)原理:

  • 利用<script>标签的 src 属性没有跨域的限制
  • 访问的地址返回一个预先定义好的 Javascript 函数的调用
  • 将服务器数据以该函数参数的形式传递给前端

(2)jsonp代码实现

function jsonp({url,params,callback}){
	return new Promise((resolve,reject)=>{
		let script = document.createElement('script')
		window[callback] = function(data){
			resolve(data)
			document.removeChild(script)
		}
		script.src = `${url}?${encodeParams(params)}`
		document.body.appendChild(script)
	})
}


function encodeParams(obj){
	const params = []
	Object.keys(obj).forEach(key=>{
		let value = obj[key]
		if(typeof value === 'undefined'){
			value = ''
		}
		params.push(`${key}=${value}`)
	})
	return params.join('&')
	//例如 当obj = {name:"老王",id:"1"}
	//返回 name=老王&id=1
}

jsonp({
	url:"http://localhost/student",  //真实情况下请求该链接返回的是一个可以返回数据的可执行函数
	params:{name:"小明",id:"1",action:"奥里给"},  //需要的参数
	callback:'show'  //对应链接中返回的函数名
}).then(data=>{
	//打印出请求该url所得到得数据
	console.log(data)
})


服务器端代码

let express = require('express')

let app = express()

app.get('/student',function(req,res){
	let {name,id,action,callback} = req.query
	console.log(name,id,aciton) // 小明,1,奥里给
	res.end(`${callback}('传入要返回的数据')`)
})

app.listen(3000)

(3)缺陷

  • 只能使用GET请求
  • 存在遭受XSS(跨域脚本攻击的风险)

2.CORS跨域资源共享
(1)原理
CORS(Cross-Origin Resource Sharing)是W3C的一个工作草案,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

(2)代码实现
首先我们运行两个简单的本地服务器
server1

let express = require('express')
let app = express()
app.use(express.static(__dirname)) //可访问静态资源
app.listen(3000)

server2

let express = require('express')

let app = express()
app.get('/getData',function(res,res){
	res.end("你被黑人兄弟抬走了")
})


app.listen(4000)

在这里插入图片描述

然后就让咱们一个一个撸过去吧!CORS实际上不常用,咳咳,但还是有掌握的必要的

  • Access-Control-Allow-Origin
...其它部分省略
<script>
		let xhr = new XMLHttpRequest;
		xhr.open('GET','http://localhost:4000/getData',true)

		xhr.onreadystatechange = function(){
			if(xhr.readyState === 4){
				if(xhr.status >= 200 && xhr.status === 304
				|| xhr.status === 300){
					console.log(xhr.response)
				}
			}
		}
		xhr.send()
	</script>
...

新建一个hmtl页面,建立XHR请求,去访问4000端后的’/getData’,由于该html文件是运行在3000端口的,此时就形成了跨域。

...省略
let whilList = ['http://localhost:3000']  //设置白名单
app.use(function(req,res,next){
	let origin = req.headers.origin
	if(whilList.includes(origin)){
		//设置哪个源可以访问
		res.setHeader('Access-Control-Allow-Origin',origin)  //设置可访问的源
	}
	next()
})

然后在浏览器界面就可以看到你被黑人兄弟抬走了

  • Access-Control-Allow-Headers
let xhr = new XMLHttpRequest;
		xhr.open('GET','http://localhost:4000/getData',true)

		xhr.setRequestHeader('name','黑人抬棺') //设置访问的请求头
		xhr.onreadystatechange = function(){
			if(xhr.readyState === 4){
				if(xhr.status >= 200 && xhr.status === 304
				|| xhr.status === 300){
					console.log(xhr.response)
				}
			}![在这里插入图片描述](https://img-blog.csdnimg.cn/20200601071345672.png#pic_center)
		}
		xhr.send()

如果你请求头改变的话,此时浏览器会报出这种错误
在这里插入图片描述

let whilList = ['http://localhost:3000']
app.use(function(req,res,next){
	let origin = req.headers.origin
	if(whilList.includes(origin)){
		//设置哪个源可以访问
		res.setHeader('Access-Control-Allow-Origin',origin)
		res.setHeader('Access-Control-Allow-Headers','name,x,x,x,') //设置多个合法的头部信息
	}
	next()
})

然后在浏览器界面就可以看到你被黑人兄弟抬走了

  • Access-Control-Allow-Methods
    从此处开始就只贴出针对上一个知识点修改过的代码了
xhr.open('PUT','http://localhost:4000/getData',true)

改变了请求的方式,此时又会看到这样的错误
在这里插入图片描述

res.setHeader('Access-Control-Allow-Methods','PUT')

然后在浏览器界面就可以看到你被黑人兄弟抬走了
在这里插入图片描述

  • Access-Control-Allow-Credentials
document.cookie = 'name=黑人抬棺'
xhr.widthCredentials = true

当请求携带cookie的时候,又会发现出错
在这里插入图片描述

res.setHeader('Access-Control-Allow-Credentials',true)

此时就能成功接收到cookie了

3.postMessage
postMessage是两个页面之间的通信
a.html

<body>
<iframe src="http://localhost:4000/b.html"
		frameborder="0" id="frame" onload="load()"></iframe>
</body>
<script>
function load(){
			let frame = document.getElementById('iframe')
			frame.contentWindow.postMessage('你要被黑人兄弟抬走了')
			window.onmessage = function(e){
				console.log(e.data) //好的,我已经躺进去了
			}
		}
</script>

b.html

window.onmessage = function(e){
			console.log(e.data) //'你要被黑人兄弟抬走了'
			e.source.postMessage('好的,我已经躺进去了')
		}

此时就可以成功的在浏览器看到你要被黑人兄弟抬走了好的,我已经躺进去了

4.window.name + iframe
前端常见跨域解决方案(全)

5.设置nginx代理
代码实例

if ($request_method = 'OPTIONS') {
  return 204;
}
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, PATCH, DELETE, HEAD" always;
add_header Access-Control-Max-Age 86400 always;

解析:
1.首先判断请求方法是否为OPTIONS,OPTIONS请求方法为预检请求,当浏览器需要跨域请求的时候,会先使用该方法请求服务器,服务器返回正确的信息之后,浏览器才会发送正式请求,(一般除了 GET 方法外的跨域请求都会先发送预检请求),预检请求的返回状态码为 204 代表成功,其它均为失败。 预检请求没有返回的 Body。

2.Access-Control-Allow-Origin,代表允许域名下的页面来请求这个地址,一般不会设置为*号,而是

add_header Access-Control-Allow-Origin www.example.com

3.Access-Control-Allow-Origin,代表允许在请求该地址的时候带上指定的请求头,例如Content-Type,Authorization,使用,号分割,放在双引号中。

add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;

4.Access-Control-Allow-Methods,代表允许使用指定的方法请求该地址

add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, PATCH, DELETE, HEAD" always;

5.Access-Control-Max-Age,代表着在限定的时间(比如上述的86400)秒之内,再次请求该地址的时候,不需要进行预检请求,也就是跨域缓存。

add_header Access-Control-Max-Age 86400 always;

上述的add_header最后都加上了always,它标识不管返回状态码是多少,都会使得add_header生效。

参考资料:
彻底理解浏览器的跨域
前端常见跨域解决方案(全)
不要再问我跨域的问题了
详解跨域(最全的解决方案)
什么是跨域?跨域解决方法

感谢阅读,欢迎批评指正,希望大家能够在追求卓越中不断进步,让优秀成为一种习惯~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值