目录

理解跨域

理解跨域

TLDR:

  1. 在响应头中加入CORS即可解决跨域问题。
request.setRequestHeader('Access-Control-Allow-Origin', );
  1. 不能使用CORS的场景下,JSONP方案也可,即前端引用不同源的js,后端返回一个包含了数据的js代码(可以是将数据写在window上或是按照前端要求使用约定的回调函数),实现跨域共享数据
//JSONP简化示意
//前端
const script = document.createElement('script')
script.src = 跨域的某个js
document.body.append(script);
//后端
 window.xxx = 数据

1. 跨域与同源策略

同源策略就是禁止跨域访问,而跨域技术就是为突破同源策略而出现的。

1.1. 源与同源:

  1. 源=协议+域名+端口号,端口号默认80
  2. window.origin或location.origin获取当前源
  3. 同源:源相同,即协议、域名、端口号都相同
    1. 注意:要完全相同
    2. https://www.baidu.com/https://baidu.com/ 不同源

1.2. 同源策略及其目的

  1. 同源策略:浏览区故意设置的功能限制,不用源的页面之间,不准互相访问数据,主要有以下三种行为收到限制
    1. Cookie、LocalStorage 和 IndexDB 无法读取
    2. DOM 无法获得
    3. AJAX 请求不能发送
  2. 同源策略(即禁止跨域访问)的目的:保护用户隐私
    1. 问题根源:黑客和正常的请求只有一个不同,就是请求的referrer(历史遗留问题,HTTP请求头中为referer,是错误拼写)
    2. 如果后端忘记检查源,那么用户数据就会被拿走,所以浏览器要主动防御这种行为

2. 跨域技术方案

2.1. CORS:Cross-origin resource sharing跨域资源共享

  1. 简单请求(不会触发CORS预检preflight的请求)
    1. 判定条件:需全部满足,这里只列出部分日常使用需注意的
      1. GET/HEAD/POST
      2. Content-Type 的值仅限于下列三者之一:
        1. text/plain
        2. multipart/form-data
        3. application/x-www-form-urlencoded
      3. 其他要求详见MDN CORS部分
//后端,添加响应头
response.setHeader( Access-Control -Allow-Origin', );
  1. 预检请求
    1. 需要预检的请求:需要使用OPTIONS方法发起预检请求到服务器以获知服务器是否允许该实际请求
//后端,添加响应头
response.setHeader('Access-Control-Allow-Origin', 'http://yangyixuan.icu'/*源,默认80端口*/)
response.setHeader('Access-Control-Allow-Methods',  'GET, POST, OPTIONS, PUT, PATCH, DELETE'/*允许的方法*/)
response.setHeader('Access-Control-Allow-Headers', 'X'/*自定义头部*/)
response.setHeader('Access-Control-Max-Age', 86400/* 86400=24*3600即一天 缓存时间*/)
  1. 附带身份凭证的CORS
    1. 当响应的是附带身份凭证的请求时,服务端必须明确 Access-Control-Allow-Origin 的值,而不能使用通配符“*”。
    2. CORS 预检请求不能包含凭据。预检请求的响应必须指定 Access-Control-Allow-Credentials: true 来表明可以携带凭据进行实际的请求。
//后端,添加响应头
res.setHeader('Access-Control-Allow-Origin', 
'http://yangyiuxan.icu'/*不能使用'*'*/)
response.setHeader('Access-Control-Allow-Credentials', true);

2.2. JSONP

  1. JSONP是不支持cors情况下(IE),使用的另一种跨域方式,即前端引用不同源的js,后端返回一个包含了数据的js代码(可以是将数据写在window上或是按照前端要求使用约定的回调函数),实现跨域共享数据
  2. 思路:
    1. 前端与后端约定回调函数/变量名
    2. 前端引用JS文件(通过script标签)
    3. 后端按照约定将数据放入相应的JS中
    4. JS文件下载并执行回调后
  3. 优点
    1. 兼容IE
  4. 缺点
    1. script标签,读不到状态码,只知道成功或失败,没有AJAX精细
    2. 只能发GET,不支持POST
  5. 注意:
    1. 为防止函数名冲突,回调名字随机生成,以callback为键的参数,随机值为值,放在请求的查询参数中
    2. JSONP约定,query中的callback对应的值为JSONP回调函数
//JSONP Promise封装
//前端
function jsonp(url) {
 return new Promise((resolve, reject) => {
   const random = "frankJSONCALLbackName=" + Math.random()
   window[random] = (data) => resolve(data)
   const script = document.createElement("script")
   script.src = `${url}?callback=${random}`
   script.onload = () => {
     script.remove()
   }
   script.onerror = () => {
     reject()
   }
   document.body.appendChild(script)
 })
jsonp("http://yangyixuan.icu/data.js")
.then(data => { console.log(data) })

//后端
// ....省略部分代码...
if (path === "/data.js") {
	response.statusCode = 200;
	response.setHeader("Content-Type", "text/javascript;charset=utf-8");
	const string = `window['{{xxx}}']({{data}}) `
	const data = fs.readFileSync("./public/friends.json").toString();
	const string2 = string.replace("{{data}}", data).replace('{{xxx}}', query.callback);
	response.write(string2);
	response.end();
}

2.3. 其他跨域方式

  1. postMessage(ie不支持,主要用于网页间通信)
  2. websocket太重了
  3. cors中间件
  4. iframe过时
  5. 后端
    1. nodejs中间件
    2. nginx反向代理