跨域问题
浏览器的同源策略导致了跨域问题
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
- 同源
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源
下表给出了相对http://store.company.com/dir/page.html
同源检测的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | |
http://store.company.com/dir/inner/another.html | 成功 | |
https://store.company.com/secure.html | 失败 | 不同协议 ( https 和 http ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同端口 ( 81 和 80) |
http://news.company.com/dir/other.html | 失败 | 不同域名 ( news 和 store ) |
跨域问题需要服务端与客户端相同配合完成,单方面并不能解决问题。
同源策略限制下接口请求的正确打开方式
JSONP
- 使用 nodejs 搭建服务端,监听 8080 端口
//通过require将http库包含到程序中
const http = require("http");
//引入url模块解析url字符串
const url = require("url");
//引入querystring模块处理query字符串
const querystring = require("querystring");
//创建新的HTTP服务器
const server = http.createServer();
//通过request事件来响应request请求
server
.on("request", function(req, res) {
let urlPath = url.parse(req.url).pathname;
// res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
res.writeHead(200, { "Content-Type": "application/json" });
let data = {
name: "Hello World"
};
if (urlPath === "/jsonp") {
if (req.url.split("?").length >= 2) {
let qs = querystring.parse(req.url.split("?")[1]);
//回调函数名为qs.callback
let callback = qs.callback + "(" + JSON.stringify(data) + ");";
res.end(callback);
} else {
data.name = "noCallbackFunction";
}
}
res.end(JSON.stringify(data));
})
//监听8080端口
.listen("8080", () => {
//服务器启动成功
console.log("Server running! listen 8080");
});
这样,我们需要的服务器搭建好了,在前端请求后加入’/jsonp?callback=functionName’,就将其认定为 JSONP 请求,且 callback 后带入一个 js 中已有的全局方法。
- 客户端使用 JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function testJSONP(data){
console.log(data);
}
</script>
<script src="http://127.0.0.1:8080/jsonp?callback=testJSONP"></script>
</body>
</html>
在浏览器控制台输出返回结果
使用 jquery 直接跨源访问,会产生错误
<script>
$(function()
{$.get("http://127.0.0.1:8080/jsonp", function(data) {
console.log(data);
})}
);
</script>
产生的错误如下:
- 客户端使用 angularJS,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<script src="https://cdn.staticfile.org/angular.js/1.4.6/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
{{name}}
</div>
<script> var app = angular.module("myApp", []); var gitApi = "https://api.github.com?callback=JSON_CALLBACK"; //callback=JSON_CALLBACK固定写法不能修改 app.controller("myCtrl", function($scope, $http) { $http({ method: "JSONP", url: gitApi }).then( function(response) { //调用成功后执行 console.log("success" + JSON.stringify(response.data)); $scope.name = response.data; }, function(response) { //调用失败后执行 console.log(response); } ); }); </script>
</body>
</html>
CORS
CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
使用 CORS 设置服务端允许跨域
- Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时 Origin 字段的值,要么是一个*,表示接受任意域名的请求。res.writeHead(200, { "Content-Type": "application/json" ,"Access-Control-Allow-Origin":"*"}); //指定允许跨源访问的资源 //res.writeHead(200, { "Content-Type": "application/json" ,"Access-Control-Allow-Origin":"http://172.0.0.1"});
- Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为 true,即表示服务器明确许可,Cookie 可以包含在请求中,一起发给服务器。这个值也只能设为 true,如果服务器不要浏览器发送 Cookie,删除该字段即可。 - Access-Control-Expose-Headers
该字段可选。CORS 请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到 6 个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。上面的例子指定,getResponseHeader('FooBar')
可以返回 FooBar 字段的值。