Cors解决跨域
在解决跨域问题之前我们先来看一看以下几个问题:
一、为什么会出现跨域
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
二、什么是跨域
当一个请求的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
当前页面url | 被请求页面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 协议不同 |
http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同 |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同 |
http://www.test.com/ | http://www.test.com:8080/ | 跨域 | 端口号不同 |
三、非同源限制
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 AJAX 请求
四、解决办法
1、CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
浏览器将CORS跨域请求分为简单请求和非简单请求
简单请求:(需同时满足以下两点)
1.请求的方式只能是GET,POST,HEAD
2.HTTP请求头是:
Accept
Accept-Language
Content-Language
Content-Type: 只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain
不同时满足上面的两个条件,就属于非简单请求。浏览器对这两种的处理,是不一样的。
非简单请求:(满足任意一点)
1.请求方式为除GET,POST,HEAD之外的Method类型
2.请求头包含自定义头端
3.向服务器发送了application/json格式的数据
非简单请求在请求CROS时,会在正式通信之前,增加一次HTTP查询请求,称为预检请求。
简单、非简单请求的区别:简单请求之发送一次请求,预检请求发送两次请求,在发送之前会先发送一次OPTION的与请求,与请求成功之后才会发送实际的请求和数据
1.CORS主要是在服务端进行配置,客户端浏览器无需做任何配置,即可请求开启了CORS的接口
2.CORS在浏览器中有兼容性
3.CORS默认情况下只支持GET、POST、HEAD,如果客户端希望通过PUT\DELETE等方式请求服务器资源,则须通过Access-Control-Allow-Methods来指明实际请求所允许的HTTP方法
res.setHEader('Access-Control-Allow- Methods','POST,GET,DELETE,HEAD')
或者
res.setHEader('Access-Control-Allow-Methods',*)
前端解决
-
原生Ajax(需和后端一起使用)
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容 // 前端设置是否带cookie xhr.withCredentials = true; xhr.open('post', 'http://www.domain2.com:8080/login', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=admin'); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } };
-
jQuery Ajax
$.ajax({ ... xhrFields: { withCredentials: true // 前端设置是否带cookie }, crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie ... });
3.vue框架
a.) axios设置:
axios.defaults.withCredentials = true
b.) vue-resource设置:
Vue.http.options.credentials = true
服务端解决
-
Nodejs(Express,前端无需设置任何东西即可解决)
/* CORS(主流方案):cors是express提供的第三方库,通过安装和配置cors中间件,可以很方便的解决跨域问题 由一系列HTTP的响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源 步骤: 1.安装: npm i cors 2.导入中间件 const cors = require("cors") 3. 配置中间件 app.use("cors()") */ const express = require('express'); const app = express(); // 配置cros跨域请求中间件 const cors = require('cors') app.use(cors()) const api = require('./api') app.get('/api/jsonp',(req,res)=>{ const fuName = req.query.callback; const data = {name:'黎明'}; let str = `${fuName}(${JSON.stringify(data)})` res.send(str) }) app.use(express.urlencoded({extended:false})) app.use('/api',api) app.use((err,req,res,next)=>{ res.send('Error'+err.message) }) app.listen(80,()=>{ console.log('http://localhost'); })
原生Node(需和前端一起使用)
var http = require('http'); var server = http.createServer(); var qs = require('querystring'); server.on('request', function(req, res) { var postData = ''; // 数据块接收中 req.addListener('data', function(chunk) { postData += chunk; }); // 数据接收完毕 req.addListener('end', function() { postData = qs.parse(postData); // 跨域后台设置 res.writeHead(200, { 'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie 'Access-Control-Allow-Origin': 'http://www.domain1.com', // 允许访问的域(协议+域名+端口) /* * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现), * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问 */ 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly的作用是让js无法读取cookie }); res.write(JSON.stringify(postData)); res.end(); }); }); server.listen('8080'); console.log('Server is running at port 8080...');