小白关于“跨域”问题的自我解释
什么是跨域
首先了解跨域是怎么造成的,主要原因就是浏览器都遵守“同源策略”,何为“同源”,即两个URL的协议、域名和端口一致则为同源。那为什么要有这个同源策略呢?
举个例子:假如有一天你要登陆某银行的网站,输入一个错误的地址后结果该地址和你要浏览的银行网站页面一摸一样(URL不一样)此时你没有发觉出来,输入了你的账号和密码之后,如果没有“同源策略”的保护,那么很有可能你这个密码和账户就被不法分子盗取了。那有了“同源策略”如何其保护呢?主要是银行网站的服务端在你浏览器想要call它的API的时候,真正的银行服务端一般只允许真正的url的域允许访问,其他的呢是不允许被访问的,那么即便假的浏览器能连到真正的服务器后端且输入正确账号和密码,因为“跨域”问题大概率也是会被档掉的。
解决跨域常见的几种方法
(1)通过jsonp跨域
(2)通过修改document.domain来跨子域
(3)使用window.name来进行跨域
(4)使用HTML5中新引进的window.postMessage方法来跨域传送数据
(5)使用CROS解决跨域问题----------服务端方案
(6)使用代理服务器解决跨域问题----------浏览器方案
使用CROS解决跨域问题
CORS全称是"跨域资源共享":因为CROS需要客户端和浏览器端同时支持,而目前最新的浏览器全部支持,因此CORS解决跨域问题主要是服务端来解决,既然依靠服务端,那么主动权就在服务端手里,他可以告诉浏览器哪些网站可以访问,哪些网站不可以访问,从而得到保护自己的资源(上述例子就是这种)
- 具体实现:CORS标准说只要在header里面增加几个关于非同源访问要求的header即可,只能要求说浏览器你看到我这个header就帮我保护一下吧,具体的对于资源的保护实际上并没有做任何事情。
1.请求时包含的headers
Origin -- 访问的源domain,之所以要有这个是因为中间可能有proxy
Access-Control-Request-Method
Access-Control-Request-Headers
2.响应时需要包含的headers
Access-Control-Allow-Origin -- -- 非同源时可以允许的domain
Access-Control-Allow-Credentials -- 非同源时使用的证书
Access-Control-Expose-Headers
Access-Control-Max-Age -- 缓存使用
Access-Control-Allow-Methods -- 非同源时可以允许的请求方法
Access-Control-Allow-Headers -- 非同源时可以允许携带的heade
方案一: 这里我们使用Express框架来解决一下跨域的问题,代码上:
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');//此中间件需要通过npm install 获得
var app = express();
var port = 2000;
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:1338'); //必须重新设置,把origin的域加上去(这里localhost:1338是静态的浏览器网址,或者相当于允许该地址浏览)
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'x-custom');
res.header('Access-Control-Allow-Credentials', 'true');//和客户端对应,必须设置以后,才能接收cookie.
next();
};
app.use(allowCrossDomain);
app.use(cookieParser());//运用cookie解析的中间件
app.use(bodyParser.text());
app.put('/', function (req, res, next) {
console.log(req.body);//打印出请求体
console.log(req.cookies);//打印出所有cookie
res.send('it is response for CORS request!');
});
app.listen(port);
此时使用浏览器访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Jsonp example</title>
<script type="text/javascript" src="./jquery/dist/jquery.js"></script>
<script type="text/javascript">
function clickIt() {
var cors = document.getElementById('cors');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:2000', true);
xhr.setRequestHeader('x-custom','xxx');
xhr.withCredentials = true;//必须设置
document.cookie ="name6=value6;domain=localhost;path=/"; //必须设置,但是不可以加端口号
xhr.onload = function (e) {
document.getElementById("result").innerHTML = xhr.responseText;
};
xhr.send('this is a CORS request!');
}
</script>
</head>
<body>
<button id="cors" onclick="clickIt()">send cors</button>
<p id="result"></p>
</body>
</html>
这样我们就解决了可以使用localhost:1338(浏览器端)访问localhost:2000(服务端)的跨域问题。当然还有一种最为简单的方式,那就是在server中使用cors包
方案二: 使用cors包
第一步:安装cors
npm install cors
第二步:在server.js中引用
var cors = require('cors');
app.use(cors());
这样所有的域就都可以访问这里的服务端了
拓展讨论
面试有个情景讨论:假设有有个集团两家分公司要同一个服务端的数据,分公司一和服务端的域名一样,分公司二在如何不修改服务端的代码的基础上该如何实现跟分公司用同一个服务端呢?——使用代理服务器的方式解决跨域(方法下回解析)