同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。--来源 MDN
简单请求
浏览器禁止跨域分为两类:
- 简单请求,在跨域请求response的时候禁止用户查看response,大部分请求都是简单请求。
- 复杂请求,在跨域请求发出的时候禁止发送请求。
简单请求跨域的特点:
- 跨域是浏览器的安全策略,如果使用爬虫直接请求并没有跨域问题。
- 跨域是浏览器禁止用户查看跨域请求的response,而不是禁止发出跨域请求。
- 跨域请求能够走到后端,后端处理完成之后前端访问不了(被浏览器block了)。
简单请求不会触发CORS预检请求。请注意,该术语并不属于Fetch(其中定义了 CORS)规范。若请求满足所有下述条件,则该请求可视为“简单请求”:
- method为GET/HEAD/POST之一
- 没有设置以下集合外的请求头:Accept、Accept-Language 、Content-Language 、Content-Type(需要注意额外的限制) 、DPR 、Downlink 、Save-Data 、Viewport-Width 、Width
- Content-Type的值仅限于下列三者之一:(例如 application/json 为非简单请求)、text/plain 、multipart/form-data 、application/x-www-form-urlencoded
- 请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器;XMLHttpRequestUpload对象可以使用XMLHttpRequest.upload属性访问。
- 请求中没有使用ReadableStream对象。
除了简单请求,别的都是复杂请求。
解决方案一:后端在response里面添加header
app.use(async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
ctx.set("Access-Control-Allow-Credentials", true);
ctx.set("Access-Control-Request-Method", "PUT,POST,GET,DELETE,OPTIONS");
ctx.set(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, cc"
);
if (ctx.method === "OPTIONS") {
ctx.status = 204;
return;
}
await next();
});
解决方案二:nginx代理
解决方案三:改webpack配置
解决方案四:jsonp
JSONP(只支持get,用一个动态script标签直接进行get请求,因为同源策略对script无效)
手写jsonp
/**
* jsonp获取请求数据
* @param {string}url
* @param {object}params
* @param {function}callback
*/
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script');
params = JSON.parse(JSON.stringify(params));
let arrs = [];
for (let key in params) {
arrs.push(`${key}=${params[key]}`);
}
arrs.push(`callback=${callback}`);
script.src = `${url}?${arrs.join('&')}`;
document.body.appendChild(script);
window[callback] = function (data) {
resolve(data);
document.body.removeChild(script);
}
})
}
其它方案
- document.domain + iframe (只有在主域相同的时候才能使用该方法)
- window.name + iframe (name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。)
- web sockets(web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。)