参考:
浏览器的同源策略
浏览器同源政策及其规避方法
同源政策
什么是同源策略?
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
源的定义
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
同源检测示例:http://www.example.com/directory/page.html
http://www.example.com/directory/other.html // 成功
http://www.example.com/directory/inner/another.html // 成功
https://example.com/index.html // 失败 不同协议(http | https)
http://example.com:90/dir/secure.html // 失败 不同端口(80 | 90)
http://new.example.com:90/dir/secure.html // 失败 不同域名(new.example | example)
同源下的脚本只能读取与所属文档同源的窗口和文档的属性。
同源政策的目的,是为了保护用户信息的安全,防止恶意的网站窃取数据。
目前,非同源将会受到限制的行为有:
- 无法读取非同源资源下的:Cookie、LocalStorage、IndexedDB。
- 无法操作非同源网页的DOM。
- 无法向非同源地址发送Ajax请求(实际上,服务器会收到请求,也会返回,但最终被浏览器拦截)。
注意
- 对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域。
- 同源策略限制的是脚本嵌入的文本来源,而不是脚本本身。
不受同源策略限制:
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
- 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的,,,
实现一个同源限制
html
Document
Hello World!
server.js
var http = require("http");
var fs = require("fs");
var path = require("path");
var url = require("url");
var server = http.createServer(function (req, res) {
var pathObj = url.parse(req.url, true);
console.log(pathObj.pathname);
switch (pathObj.pathname) {
case "/getSomething":
res.end(
JSON.stringify({ beijing: "sunny" })
);
break;
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function (err, data) {
if (err) {
res.writeHead(404, "not found");
res.end("404 Not Found
")
} else {
res.end(data);
}
})
break;
}
});
server.listen(8080);
console.log("visit http://localhost:8080/httpServer.html");
HTML页面中的Ajax请求就是test point,页面中,我们设置了一个Ajax请求,并在node server中的switch语句进行设定,当请求发送后,如果有一个指向
http://localhost:8080/getSomething
的请求,本地服务器就会返回一个被JSON.stringify()方法解析后的JS对象。否则,就使用fs模块读取静态文件(css、js等)
正常访问成功下的请求状况:
每一个文件都显示
200 ok
,请求成功,并且服务器也收到了Ajax请求,并返回数据。
我们稍微对Ajax请求URL更改一下:
http://www.localhost:8080/getSomething
运行服务器:
此时,我们就实现了一个跨域请求,只是没有数据返回。
我们发现,HTTP状态码显示200,说明请求是成功的,但却没有数据返回,且有红字报错。
Failed to load http://www.localhost:8080/getSomething: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
出现这段提示,就是浏览器告诉你,跨域了!
No 'Access-Control-Allow-Origin' header is present on the requested resource
意思是不存在Access-Control-Allow-Origin
标记,这个标记是我们实现跨域时才会出现的,如果存在此标记,浏览器不会对跨域的请求进行拦截,稍后会详细解释。
注意:请求是成功的,但是因为浏览器的安全机制,请求的数据被拦截。
跨域 —— JSONP
JSONP是JSON with padding,填充式JSON或参数式JSON的缩写。是应用JSON的一种新方法,在后来的Web服务器中非常流行。JSONP看起来与JSON差不多,只不过是被包含在函数调用中的JSON,就像下面这样。
callback({"name": "Nicholas"});
JSONP由两部分组成:
- 回调函数
- 数据
回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数的JSON数据。
http://freegeoip.net/json/?callback=handleResponse
这个URL请求在请求一个JSONP地理定位服务。通过查询字符串来指定JSONP服务的回调函数是很常见的,就像上面URL所示,这里指定的回调函数名字叫handleResponse()
。
这个请求到达后端后,后端回去解析callback这个参数获取到字符串handleResponse,在发送数据做以下处理:
假设之前后端返回数据: {"city": "hangzhou", "weather": "晴天"}
,现在后端返回数据: handleResponse({"city": "hangzhou", "weather": "晴天"})
前端script标签在加载数据后会把 handleResponse({"city": "hangzhou", "weather": "晴天"})
做为 js 来执行,这实际上就是调用handleResponse
这个函数,同时参数是{"city": "hangzhou", "weather": "晴天"}
。 用户只需要在加载提前在页面定义好handleResponse
这个全局函数,在函数内部处理参数即可。
总结:
JSONP是通过 script 标签加载数据的方式去获取数据当做 JS 代码来执行 提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP 需要对应接口的后端的配合才能实现。
栗子
HTML
Document
我们创建了一个