前段时间采用Vuejs前端框架+Element-ui组件库+Flask后端框架写了一个技术栈涵盖前后端+数据库的项目,在其中总结了大量知识,为了加深理解,决定分批次深入学习并总结下。
其中一个重要问题,便是本篇要总结的跨域问题。
在vue
的开发过程中,需要利用请求访问后端api
,完成数据的交互和功能的触发,此时vue
与flask间为不同端口,产生了如下错误。
跨域源请求拦截问题CORS头缺少’Access-Control-Allow-Origin’
这是受限于浏览器的同源策略影响而产生的。
同源策略是浏览器(不是vue
框架本身)的安全限制,为了保护用户的安全,防止跨域的数据交互和资源获取。在生产环境中,如果是不可信任的源,理所当然要进行授权。
当两个服务的协议、域名(一级等)、端口号三者完全相同时,我们才称他们同源。
当这些任意一项不同时,他们之间是不同源(跨域)的。
广义上来讲,许多资源都是非同源的,然而大部分情况下,html、css、js等跨域资源均可获得,因为它们的加载得到了浏览器的允许。狭义上,跨域主要指Ajax请求的跨域。
满足同源策略要求,文件在同一域名下(即使是不同文件夹)
协议不同
http://www.a.com/api1/
https://www.b.com/api2/
端口不同
http://127.0.0.1:2000/api1/
http://127.0.0.1:2001/api2/
域名的不同
一级域名不同,即完全不是一个站。
二级域名不同
主域名的不同子域
http://子域.主域(二级域名).顶级域名/xxx
http://www.abc.com/api1/
http://doc.abc.com/api2/
和它的对应ip不同
http://[a的域名]/api1/
http://[a的ip]/api2/\
A无法访问B的cookie、无法发送Ajax请求,也无法操纵对方的DOM
利用了**js
访问和调用**不受同源策略限制的特性(script标签的开放策略),但仍需要服务器端的支持。
其实现原理和方法,请自行查看。
此处不推荐的原因是,它会提高前后端的耦合度,数据量小,且安全性低。
vue
中使用了$http的JSONP请求模式(GET请求)。
this.$http.jsonp("http://localhost/api1")
.then(function (reponse) {
console.log(reponse);
})
CORS:全程Croess-origin resourse sharing
,运行浏览器向跨域服务器发送Ajax请求,克服了Ajax只能使用同源的限制。
优点:jsonp
是get形式,承载的信息量有限,信息量较大时CORS更优。
原理比较简单,通过在目标域名返回 CORS 响应头来达到获取该域名的数据.
设置:只需要在后端设置响应头即可,也就是设置 response header
分类: 简单请求(simple request)和非简单请求或预检请求(not-so-simple request)
简单请求
:后端至少响应 Access-Control-Allow-Origin: 目标源地址即可;
前端发送AJAX请求时,自动添加Origin字段,后端返回包含Access-Control-Allow-Origin
的响应,则代表Origin包含在Access-Control-Allow-Origin
中,即可完成跨域资源共享,否则,后端会返回不包含该字段的正常HTTP请求,则前端XMLHttpRequest
产生错误。
非简单请求
:两步,浏览器会在正式通信之前,进行OPTIONS方式的HTTP查询请求,后端检查了Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,确认允许跨源请求,发出携带了CORS相关的头信息字段的响应(如,Access-Control-Allow-Origin: *
),则前端浏览器认为服务器同意,否则表示服务器不同意预检请求,此时前端控制台会出现我们熟悉的:
XMLHttpRequest cannot load http://localhost
Origin http://localhost is not allowed by Access-Control-Allow-Origin.
第二步正式请求,和简单请求相同。
服务器返回的CORS相关字段:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: content-type… 客户端要返回的Header
Access-Control-Allow-Credentials: true 表示是否允许发送Cookie
Access-Control-Max-Age: 1728000 预检有效时长
Access-Control-Allow-Origin: * 接受某域名的请求
Access-Control-Expose-Headers
// php为例
header("Access-Control-Allow-Origin","源地址";
header("Access-Control-Allow-Credentials", "true");
vue
端src/main.js
import axios from 'axios'; // 从node_modules目录中导入包
axios.defaults.withCredentials = false; // 为true则要求ajax发送请求时附带cookie,此时后端设置Access-Control-Allow-Origin不能为 " * ",要为源地址。
Vue.prototype.$axios = axios; // 把对象挂载vue中,可以设置为全局
Flask也包含了跨域的包,Flask-CORS包
Python - Flask - Cors
跨域
Python - django
- 跨域CORS - 赵~刚 - 博客园
我们知道,服务器一般情况下是没有同源限制的,利用目标服务器作为代理服务器,总揽全部和用户交互的工作,用户只需和代理服务器交互,因此浏览器只需要和同一源交互即可,根本上避免了跨域问题。
例如,搭载在统一服务器上的、不同端口的两个应用,利用代理服务器转发,就可以通过同一端口下的特定路径和转发服务访问不同端口,同时显式上又不违反同源策略,即可完成跨域访问。
http {
server {
listen 80; # 监听的端口
server_name localhost; # 当前域名
location / {
proxy_pass http://localhost:80/;
proxy_redirect default;
}
location /api1 {
rewrite ^/api1/(.*)$ /$1 break;
proxy_pass http://localhost:8888/;
}
location /api2 {
rewrite ^/api2/(.*)$ /$1 break;
proxy_pass http://localhost:9999/;
}
}
}
访问80下的对应路径,等效转发访问对应端口,访问api1会将请求转发到8888端口,api2会转发到9999。
并且利用正则表达式,可以将以apix开头的请求,转发到对应的端口上。
我们进一步讲,就可以利用转发服务器作为中间服务器,将请求转发到搭载其他服务的服务器上了。利用这种方法,我们实际上是将浏览器眼中的两个域通过转发服务器划为了一个域完成的。
vue
的解决方案Vue
服务器本身也提供代理服务器方案,可以代为转发请求
main/index.js
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
// 用‘/’开头,代理所有请求到目标服务器
'/api1': {
target: 'http://127.0.0.1:1234/', // 接口域名
changeOrigin: true, // 是否启用跨域
},
'/api2':{
target: 'http://127.0.0.1:1234/', // 接口域名
changeOrigin: true, // 是否启用跨域
}
},
// Various Dev Server settings
host: '127.0.0.1', // can be overwritten by process.env.HOST
port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-