@yangfch3
2016-02-18T07:09:10.000000Z
字数 6796
阅读 4342
JavaScript
只要协议、域名、端口有任何一个不同,都被视为不同的域。
跨域问题的出现是因为浏览器的安全机制导致的,防止 AJAX 形式的跨站恶意脚本攻击,常见出现跨域问题的情景是:
font-face 字体下载AJAX 跨域请求资源例如:aspx、php、jsp 等服务器端处理程序,都有相应的组件能访问 URL 获取与处理 response,我们可以在服务器端写一个小程序用于获取外域的数据,再对数据进行相应的处理作为返回值,当我们请求这个服务器端程序对应的 URL 时,服务器作为中间人代为请求数据然后返回。
例子:梨山宾馆的天气数据获取方法
浏览器端:AJAX 请求服务器端 aspx 程序
var xhr;var rootURL = "http://www.lishanguesthouse.com.tw/";if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else if (window.ActiveXObject()) {xhr = new ActiveXObject("Microsoft XMLHTTP");}var url=rootURL + "GetWeather.ashx";xhr.onreadystatechange = success;xhr.open("POST", url, true);xhr.send();function success() {if (xhr.readyState == 4 && xhr.status == 200) {var str = xhr.responseText;str = '(' + str + ')';var obj = eval(str);var weatherIcon = document.getElementById("weatherIcon");var liTemp = document.getElementById("temperature");var iconNum = obj['img1'];var tempVal = obj['temp1'];var weatherIconUrl;if (iconNum.length==1) {weatherIconUrl = rootURL+"/Images/weatherIcon/0" + iconNum + ".png";} else {weatherIconUrl = rootURL+"/Images/weatherIcon/" + iconNum + ".png";}weatherIcon.style.backgroundImage = "url('" + weatherIconUrl + "')";liTemp.innerHTML = tempVal+" ℃";}}
服务器端的 aspx 处理程序:请求 HTML 页面,正则处理,抽取数据,生成 JSON 格式数据,返回
<%@ WebHandler Language="C#" Class="GetWeather" %>using System;using System.IO;using System.Net;using System.Text;using System.Text.RegularExpressions;using System.Web;public class GetWeather : IHttpHandler {public void ProcessRequest (HttpContext context) {//首先获取天气图标的编号,用正则表达式匹配,天气图标数据从中国天气网API获取string weatherIconUrl = "http://www.weather.com.cn/data/cityinfo/101340401.html";//根据Url返回对应网页的数据string weatherIconPage = GetPage(weatherIconUrl);//获得img1的索引int weatherIconIndex = weatherIconPage.IndexOf("img1");//获得img1对应的天气图标的编号string weatherIconNum = weatherIconPage.Substring(weatherIconIndex + 8, 1);//以下部分是把网页的table中的气温数据取出来string tempUrl = "http://www.cwb.gov.tw/V7/observe/24real/Data/C0F86.htm";string tempPage = GetPage(tempUrl);int tableStartIndex = tempPage.IndexOf("<table");int tableEndIndex = tempPage.IndexOf("</table>");string tempTable = tempPage.Substring(tableStartIndex, tableEndIndex - tableStartIndex + 8);Regex regExp = new Regex(@"<tr[^>]*>[\s\S]*?<\/tr>");MatchCollection matches = regExp.Matches(tempTable);string firstTr = matches[1].Value.ToString();Regex reg = new Regex(@"<td[^>]*>[\s\S]*?<\/td>");MatchCollection tdMatches = reg.Matches(firstTr);string firstTd = tdMatches[0].Value.ToString();string[] seperators = new string[] { "<", ">" };string[] components = firstTd.Split(seperators, StringSplitOptions.RemoveEmptyEntries);string temp1 = components[1];string weatherJson = @"{'img1':'" + weatherIconNum + "','temp1':'" + temp1 + "'}";context.Response.Write(weatherJson);context.Response.End();}public string GetPage(string url){HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);HttpWebResponse response = (HttpWebResponse)request.GetResponse();Stream responseStream = response.GetResponseStream();string page = string.Empty;StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);page = streamReader.ReadToEnd();return page;}public bool IsReusable {get {return false;}}}
在同源域名下架设一个代理服务器来转发,JavaScript 负责把请求发送到代理服务器:
'/proxy?url=http://www.sina.com.cn'
代理服务器再把结果返回,这样就遵守了浏览器的同源策略。
用脚趾头也能想到这个方法的缺点:你有这个预算搭建代理服务器么?你有这个能力在别人家的域名下架设代理服务器么?
Flash 是强大的,虽然安全性与性能问题饱受人诟病。
随着 Flash 的式微,已经极少采用。
Javascript 资源JSONP 支持,只支持 GET 请求JSONP 采用在网页中动态插入 script 元素的做法(避开 AJAX 请求,直接请求外部脚本资源),向服务器请求脚本文件。使用 GET 的方式发送请求,并需要在 URL 后面加上参数 ?callback=foo(具体见各个 API 提供方的调用说明),服务器会将 JSON 数据作为一个对象参数放到回调函数 foo 内,返回脚本。
服务器端需要的相应机制:针对特定 GET 请求的 URL,请求数据,将数据作为对象参数传入指定的回调函数,构建包含回调函数的脚本,返回脚本,JSONP 请求完成。
JSONP 作用过程:
在脚本程序内准备好返回数据的处理函数(需要预先知道返回的数据的格式)
foo(data){...}创建
script标签,向目标域目标 URL 发起请求,一般需要添加查询参数类似?callback=foo。服务器处理请求,返回数据作为参数的回调函数的脚本文件
浏览器加载好
JSONP请求的脚本,执行预先准备好的回调函数
实例:服务器端
<script type="text/javascript">function dosomething(jsondata){// 处理获得的json数据}</script><script src="http://example.com/data.php?callback=dosomething"></script>
实例:服务器端处理
<?php$callback = $_GET['callback'];//得到回调函数名$data = array('a','b','c');//要返回的数据echo $callback.'('.json_encode($data).')';//输出?>
优点:对数据格式无限制 ,服务器可自定义接收多种请求方式:GET POST OPTIONS PUT(JSONP 只支持 GET 请求),可以得到更详细的错误信息,部署更有针对性的错误处理代码
缺点:需要服务器开放请求许可,决定权仍旧在服务器手里,浏览器需支持 HTML5,在老式浏览器下表现不佳
CORS 的原理其实很简单,就是自动增加一条 HTTP 头信息的查询,询问服务器端,当前请求的域名是否在许可名单之中,以及可以使用哪些 HTTP 动作。
可见,跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的 Access-Control-Allow-Origin,决定权始终在对方手中。
简单跨域请求 的请求头与响应头信息(GET、HEAD 以及 Content-Type 类型
为 application/x-www-form-urlencoded 、multipart/form-data 和 text/plain 的 post 请求):
// AJAX Request HeaderOrigin: http://www.example.com// AJAX Response HeaderAccess-Control-Allow-Origin: http://www.example.comAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHERAccess-Control-Max-Age: 1728000
需要预检的复杂跨域请求 的请求头与响应头信息(PUT、DELETE 以及 其他类型如 application/json 的 POST 请求)。
在发送AJAX请求之前,浏览器会先发送一个 OPTIONS 请求(称为 preflighted 请求)到这个 URL 上,询问目标服务器是否接受:
OPTIONS /path/to/resource HTTP/1.1Host: bar.comOrigin: http://my.comAccess-Control-Request-Method: POST
服务器必须响应并明确指出允许的Method:
HTTP/1.1 200 OKAccess-Control-Allow-Origin: http://my.comAccess-Control-Allow-Methods: POST, GET, PUT, OPTIONSAccess-Control-Max-Age: 86400
浏览器确认服务器响应的 Access-Control-Allow-Methods 头确实包含将要发送的 AJAX 请求的 Method,才会继续发送 AJAX,否则,抛出一个错误。
// AJAX Prefilght Request HeaderOPTIONS /resources/post-here/ HTTP/1.1Host: www.google.comOrigin: http://www.example.comAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: X-PINGOTHER// AJAX Response Header for Preflight RequestHTTP/1.1 200 OKAccess-Control-Allow-Origin: http://www.example.comAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHERAccess-Control-Max-Age: 1728000// AJAX Request after Passed Preflight...
解释:
Access-Control-Allow-Origin 为 * 或域名时说明允许跨域请求Access-Control-Request-Headers 为允许的自定义请求头Access-Control-Max-Age 为预检过期时间,超过此时间需要再次预检CORS 机制默认不发送 cookie 和 HTTP 认证信息,除非在 Ajax 请求中打开 withCredentials 属性。
var request = new XMLHttpRequest();request.withCredentials = true;
同时,服务器返回 HTTP 头信息时,也必须打开 Access-Control-Allow-Credentials 选项。否则,浏览器会忽略服务器返回的回应。
Access-Control-Allow-Credentials: true
由于整个过程都是浏览器自动后台完成,不用用户参与,所以对于开发者来说,使用 Ajax 跨域请求与同域请求没有区别,代码完全一样。但是,这需要服务器的支持,所以在使用 CORS 之前,要查看一下所请求的网站是否支持。
document.domain 属性可写,但不能设置为当前 URL 中不包含的域,通过设置 domain 属性相同可实现框架间不同子域页面的 JavaScript 通信
// example.comdocument.domain = 'example.com';// b.example.com as a frame in example.comdocument.domain = 'example.com';// 可以使用程序从 example.com 访问do sth Cross-domain
document.domain 只能往上级域名方向设置,设置为非上级域名会报错,不能设置为顶级域名
// a.example.comdocument.domain = 'example.com'; // 'example.com'document.domian = 'b.example.com'; // 'Uncaught DOMException: ...'
利用的机制是框架间的 window.name 共享机制,利用 window.name 来传输数据。
此方法,也需要两个不同域的页面相互配合。
window.postMessage(message,targetOrigin) 方法是 HTML5 新引进的特性,可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源。
IE8+ 已支持。
此方法也需要两个不同域的页面相互配合!