跨域
同源策略
我们把协议、域名、端口号都相同的两个 URL 称为同源,在网页中不同源的交互通常可以分为以下三类:
- 跨域写操作(Cross-origin writes)一般是被允许的。例如链接(links),重定向以及表单提交。
- 跨域源嵌入(Cross-origin embedding)一般是被允许,比如 script、img 等
- 跨域读操作(Cross-origin reads)一般是不被允许的
当受到同源策略限制时,浏览器会提示资源被限制访问 blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource,并提示需要在响应头提供 Access-Control-Allow-Origin
字段,可以用以下的解决方案来解决跨域问题
CORS
跨域资源共享(CORS)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他 origin,从而使浏览器可以访问加载这些资源。
cors 需要浏览器浏览器和服务器同时支持,IE 浏览器不能低于 IE 10。
浏览器将 CORS 分为了简单请求和非简单请求
简单请求
对于简单请求,浏览器直接发出 CORS 请求。会在请求头上,增加 Origin 字段,用来标示请求源(协议+域名+端口)。
如果 Origin 指定的源,不在许可范围内的源,会返回一个正常的 HTTP 回应。浏览器发现响应头没有包含Access-Control-Allow-Origin
字段,就会被同源策略拦截
所以要想不被同源策略拦截,需要在响应头上加上 Access-Control-Allow-Origin
字段,可以是 *
,表示接受任意域名的请求,也可以是指定的可访问域名,以 koa 为例
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Origin", "http://localhost:8080")
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
非简单请求的 CORS 之前,会增加一个“预检”请求(preflight),这是浏览器自动发起的 options 请求,无需开发者手动操作。
所以需要服务器额外对OPTIONS
方法进行额外的处理,以 koa 为例
ctx.set("Access-Control-Allow-Origin", "http://192.168.31.168:8080")
ctx.set("Access-Control-Allow-Methods", "GET,POST,PUT")
需要在响应头设置Access-Control-Allow-Origin
指定允许跨域的源,接着设置Access-Control-Allow-Methods
字段,即允许这个方法跨域
在这个 options 方法做出了响应之后,浏览器就会发出实际的请求出来了,服务器做出相应的处理即可
JSONP
script 脚本是不会受到同源策略影响的,所以我们也可以使用 script 脚本来实现跨域,这就叫做 JSONP,例如
<script src="http://localhost:3000/?callback=back"></script>
我们封装一个更通用的函数来实现 JSONP
jsonp(url, callback, fn) {
let script = document.createElement("script")
script.src = `${url}/?${callback}=back`
window.back = function(data) {
fn(data)
}
document.body.appendChild(script)
},
function fn(res) {
console.log(res)
}
jsonp("http://localhost:3000/", "callback", fn)
然后在服务端做相应的处理,以 koa 为例,在 get 的处理中
let { query } = ctx.request
let { callback } = query
ctx.body = `${callback}(124)`