跨域

51 阅读4分钟

老生常谈

一 前置认知

域名的组成 同源:协议、域名、端口三者一致即为同源,即便两个不同的域名指向同一个ip地址,也非同源。

同源策略:一种约定,它是浏览器最核心也最基本的安全功能(非Http协议),如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。

跨域:协议、域名、端口三者之中任意一个不同,都算作不同域。不同域之间相互请求资源,就算作“跨域”。

同源策略的最常见限制:axios请求发出后,结果被浏览器拦截了 ps: img、link、script标签允许跨域加载资源

二 一些跨域解决方案

Jsonp

上古时期的老方案了

主要是利用script标签没有跨域限制的特性,从其他来源动态获得JSON数据,Jsonp兼容性好,但是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

实现过程

  1. 声明一个回调函数,将其函数名当作参数值,传递给跨域请求数据的服务器
  2. 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名
  3. 服务器接收到请求后,将传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('I love you')
  4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作

CORS跨域资源共享

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

CORS 规定了三种不同的交互模式

  • 简单请求
  • 需要预检的请求
  • 附带身份凭证的请求

这三种模式从上到下层层递进,请求可以做的事越来越多,要求也越来越严格。

简单请求

简单请求的前置条件

  1. 请求方法属于get、post、head
  2. 请求头仅包含安全的字段(Accept、Content。。。)
  3. 请求头如果包含Content-Type,仅限下面的值之一:
  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

需要预检的请求

流程

  1. 浏览器发送预检请求,询问服务器是否允许
  2. 服务器允许
  3. 浏览器发送真实请求
  4. 服务器完成真实的响应

例如一个预检请求

截屏2022-09-29 21.37.24.png 预检请求有以下特征:

  • 请求方法为OPTIONS

  • 没有请求体

  • 请求头中包含

    • Origin:请求的源,和简单请求的含义一致
    • Access-Control-Request-Method:后续的真实请求将使用的请求方法
    • Access-Control-Request-Headers:后续的真实请求会改动的请求头

预检成功后,服务器回回应对应的预检响应,然后在浏览器会发送真实的数据请求,服务器进行回应(很符合http请求的特点)

附带身份凭证的请求

默认情况下,ajax 的跨域请求并不会附带 cookie 不过设置XMLHttpRequest中的withCredentials为true就可以让该请求就是一个附带身份凭证的请求

代理模式

目前前端主流框架在开发模式下解决跨域的最常见方案。 可分为:

  • Nginx

反向代理 (同源策略仅限于浏览器,服务器无限制)

   ## 假如页面a在www.198.com, 而服务器在www.199.com, 因为同源策略的限制,页面a发起的请求会跨域,但是通过nginx配置一个代理服务器,域名和a页面相同,都是www.198.com,反向代理访问www.199.com的接口
   # Nginx代理服务器
server {
  listen       80;
  server_name  www.198.com;

  location / {
    # 反向代理地址
    proxy_pass   http://www.199.com:97;  
    # 修改Cookie中域名
    proxy_cookie_domain www.198.com www.199.com; 
    index  index.html index.htm;
		
    # 前端跨域携带了Cookie,所以Allow-Origin配置不可为*
    add_header Access-Control-Allow-Origin http://www.hahaha.com;  
    add_header Access-Control-Allow-Credentials true;
  }
}
  • Proxy

    例如Vue-CLI 是基于 webpack 的,通过 webpack-dev-server 在本地启动脚手架,也就是在本地启动了一个 Node 服务,来实时监听和打包编译静态资源,由于都是封装好的,只需要配置即可,我们在 vue.config.js 中配置代理如下,写法很多,列几个常见的自行选择

module.exports = { 
//... 
devServer: { 
    proxy: { 
    '/api': 'http://www.199.com' 
    }
   }
}