关于 AJAX 请求跨域问题在 Vue 项目中的解决方式
0. 前言
-
此文为88岁高龄、入门级前端初心者专用笔记;
-
暂时只关心 AJAX 请求受同源策略的影响及在 Vue 项目中的 解决方式 捏;
1. 必要性
1.0 你需要知道
(1)协议、域名、端口都相同,才为同源;
(2)浏览器报跨域错误,并不是服务器没有返回,而是服务器返回了,但是被浏览器拦截了!!!
(3)所谓的端口,其实可以简单地视作为进程 ID;端口的存在的就是因为服务器响应请求时需要找到具体的进程来响应,但是依靠进程 ID 又太过于依赖操作系统,每次都会发生变化而且无法通知想联系它的客户端,所以干脆统一安排一个窗口,让大家忽略细节,需要某类服务时就找那个窗口就行。
1.1 目的
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
AJAX 请求响应受限制很好理解,对于 Cookie,设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
1.2 限制范围
目前,如果非同源,共有三种行为受到同源策略限制:
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) 无法获得 DOM 进行操作 ,这样可以防止一些恶意网站在自己的网站中利用 iframe 嵌入正规的网站并迷惑用户,以此来达到窃取用户信息。
(3) AJAX 请求不能正常响应。
不受同源策略限制的(即跨域但不会出现问题):
(1) 原生 Form 表单提交。
(2) 默认情况下的静态资源的获取。
静态资源是指在服务器端存储的不会变化的文件,如HTML、CSS、JavaScript、图片、音频、视频等文件。这些文件一般不包含动态内容,每次请求时返回的内容都是固定的。
在后端服务器中,可以设置特定的路由来处理静态资源的访问请求。
例如,使用 Node.js 的 Express 框架可以使用 express.static 中间件来处理静态资源的请求。
将静态资源直接编辑放到服务器的指定目录下。当用户访问该服务器时,可以通过 URL 直接访问到这些静态资源。这种方法适用于小型项目或者对访问速度要求不高的场景。
前端代码:图片 HTML <img />
就是获取静态资源的加载,默认情况下,请求不同源的静态资源不会有跨域问题,且浏览器是用 GET 请求方法获取的。
但 “GET 请求是没有跨域问题的”这一说法是错误的,只是上述情况不会有跨域问题,在以下两种情况会有:
等获取静态资源标签的 crossOrigin 的属性不为 'use-credentials';
AJAX 发起的 GET 请求;
2. 项目中跨域问题的解决方式
2.1 本地
2.1.1 Vue proxy 正向代理
开发时,npm run dev
等命令会在本地启动一个开发服务器;可以在 vue.config.js
里的 devServer 配置开发服务器。
devServer: {
// host:'0.0.0.0', // 设置为您的 IP 地址,设置项目可用局域网 IP 地址访问
port: 8013,
proxy: {
"/api": {
target: process.env.VUE_APP_BASE_API,
changeOrigin: true, // 见后文2.1.4
pathRewrite: {
"^/api": "api"
}
}
// ...
1. "/api"的作用
用代理首先你得有一个标识,表明请求路径中有这个标识的 ajax 请求要使用当前代理。
2. pathRewrite中 "^/api":"xxx"的作用
当实际需要请求的路径里面没有“/api”时,就需要 pathRewrite,用"^/api":"",把"/api"去掉,这样既能有正确标识, 又能在请求到正确的路径。
2.1.2 配置后端接口地址
1. 在 .env.production
和 .env.development
中配置开发和生产环境的接口地址
VUE_APP_BASE_API = 'http://localhost:8003'
VUE_APP_BASE_API = 'http://xxxx:xxxx'
2. 在 request.js 中配置 ajax 请求的 baseURL
// 创建axios实例
const service = axios.create({
// axios中请求配置有 baseURL 选项,这样所有的请求都会自动加上该基础路径。
baseURL: process.env.VUE_APP_BASE_API
})
2.1.3 Vue proxy 解决原理
1. 在浏览器地址栏通过 URL 直接访问一个地址时,不存在跨域问题,且单页面 (== 关于首页的设置?单页面路由跳转?==)。
直接通过地址栏输入发起请求,对于服务器而言,这就是一个正常的浏览器请求,并不涉及到另一个域,也不存在一些脚本攻击,所以浏览器并不会对这种请求加以限制。
2. 代理服务器解决 AJAX 请求时的跨域问题。
跨域,是当前域和目标服务域之间的,在某个页面向后端发起请求时,才会产生跨域;
不配置代理,请求直接由浏览器发送给后台服务,不同源时,产生跨域问题;
配置好 proxy 后,npm run dev ==> "dev": "vue-cli-service serve"
,webpack 在本地启动一个代理服务器,我们写的页面为本地代理服务器上的静态资源,且对目标服务器的请求会被代理中间件拦截转发;见下文模拟:
// 模拟 devServer 配置代理后,前端文件及代理服务
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
// 对/api开头的请求进行转发处理的中间件
// 1. 拦截
app.use(
'/api', //表示请求的接口以/api开头
// 2. 转发
proxy({
target: 'http://localhost:8003', // 目标服务器
changeOrigin: true, // 改变 服务端接收时的 host 请求头,请求的资源的网络主机(localhost:8013) ==> target(http://localhost:8003)
})
);
// 将前端项目所在的文件夹设置为静态资源
app.use('/', express.static('./index'));
app.listen(8013);
// proxy的路径匹配
// proxy({...}) - 匹配任意路径,所有的请求都会被代理。
// proxy('/', {...})-匹配任意路径,所有的请求都会被代理。
// proxy('/api', {...})-匹配所有以/api开始的路径。
2.1.4 Host、Referer、Origin
- Host:当前请求资源的网络主机
- Referer:当前请求资源所在页面的完整路径(不包括hash),关于 vue 的??
- Origin:出现在跨域请求中,当前请求资源所在页面的协议、域名和端口,用来说明请求从哪里发起的
浏览器网络面板观察不到 host 的修改
因为浏览器运行的 js 代码,发出了一个网络请求,这个请求的 host 就是 localhost:8003。
发出去之后,被本地的代理服务拦截到了,在这个地方将 host 修改了,再发往后端服务器。
也就是,host 修改时,浏览器已经将请求发出去了,所以观察不到。
2.3 远程服务器
2.2的方法只能在开发时使用,纯前端是无法解决跨域问题的。
浏览器 URL 访问 Web 应用的地址(部署所在服务器外网 IP + 对应协议 port)
前端文件部署在 Nginx 或 Tomcat 服务器上,Web 服务器配置允许跨域即可。
2.3.1 Tomcat
修改 web.xml ,都什么年代了还在前后端不分离部署!嗨呀是我们呀,但不管暂时看不懂捏。
2.3.2 Nginx 反向代理(服务端的不都是CORS吗?)
nginx.conf
location / {
# 目标地址
proxy_pass http://localhost:8003/;
# 设置是否允许 Cookie 传输
add_header Access-Control-Allow-Credentials true;
# 允许请求地址跨域 * 做为通配符
add_header Access-Control-Allow-Origin *;
# 允许跨域的请求方法
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}