重学 JS | 跨域的原因和解决方案
了解跨域之前,我们先熟悉下浏览器同源策略。
浏览器同源策略
同源策略,即为相同协议、域名和端口号,若存在任何一点不同,而为非同源。它是浏览器最基本也是最核心的安全功能,约定客户端脚本在没有明确授权情况下,不能读写不同源的目标资源。主要存在 2 种表现形式:
DOM 同源测试
禁止对不同页面进行 dom 操作,主要场景是 iframe 跨域,不同域名下的 iframe 是会限制访问的。
XMLHttpRequest
禁止使用 XMLHttpRequest 向不同源的服务器发送 ajax 请求,防止跨站请求伪造 CSRF。
浏览器跨域解决
当没有遵守浏览器的同源策略的时候,会导致跨域限制,很大程度上这也保护了用户隐私数据的安全。但是在某些业务场景中,不可避免的需要进行跨域访问,这怎么解决呢?
1. CORS
CORS 全称“ 跨域资源共享”,是 HTML5 支持的协议,允许浏览器向跨源服务器发起 XMLHttpRequest 请求。它通过服务器增加特殊的 Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持 CORS、并且判断 Origin 通过的话,则允许 XMLHTTPRequest 发起跨域请求。这就需要服务端通过对响应头的设置,接收跨域请求处理。这里以 java 为例配置跨域:
配置全局过滤器
配置全局拦截器
单个请求的跨域通过
CORS 把 Http 请求分为两类,不同类别按不同的策略进行跨域资源共享协商。
简单的跨域请求
请求方法是 GET、HEAD 或者 POST,并且当请求方法是 POST 时,Content-Type 必须是 application/x-www-form-urlencoded, multipart/form-data 或着 text/plain 中的一个值。
请求中没有自定义 HTTP 头部。
浏览器要做的就是在 HTTP 请求中添加 Origin Header,将 JavaScript 脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加 Access-Control-Allow-Origin Header。浏览器收到响应后,查看 Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给 JavaScript。否则浏览器忽略此次响应。
带预检(preflighted)的跨域请求
除 GET、HEAD 和 POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他 HTTP 方法。
请求中出现自定义 HTTP 头部。
带预检(Preflighted)的跨域请求需要浏览器在发送真实 HTTP 请求之前先发送一个 OPTIONS 的预检请求,检测服务器端是否支持真实请求进行跨域资源访问,真实请求的信息在 OPTIONS 请求中通过 Access-Control-Request-Method Header 和 Access-Control-Request-Headers Header 描述,此外与简单跨域请求一样,浏览器也会添加 Origin Header。服务器端接到预检请求后,根据资源权限配置,在响应头中放入 Access-Control-Allow-Origin Header、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers Header,分别表示允许跨域资源请求的域、请求方法和请求头。此外,服务器端还可以加入 Access-Control-Max-Age Header,允许浏览器在指定时间内,无需再发送预检请求进行协商,直接用本次协商结果即可。浏览器根据 OPTIONS 请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。这个过程对真实请求的调用者来说是透明的。
XMLHttpRequest 支持通过 withCredentials 属性实现在跨域请求携带身份信息(Credential,例如 Cookie 或者 HTTP 认证信息)。浏览器将携带 Cookie Header 的请求发送到服务器端后,如果服务器没有响应 Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。
2. JSOP
JSONP 是利用 script 标签的 src 属性不受同源策略约束来跨域获取数据的,它实现跨域的特点是简单适用、兼容老浏览器、对服务端影响小。缺点是需要后端配置输出特定的返回信息,并且只支持 Get 请求,并且回调函数必须是全局的,因为服务端返回后会从全局环境开始找回调函数。
实现步骤分为 2 步:
在网页中动态创建 script 标签,通过 script 标签向服务器发起请求,在请求中携带一个请求的 callback 回调函数。
在服务器接收到请求后,会处理响应获取返回的参数,然后将参数放在 callback 回调函数中对应的位置,并将 callback 回调函数通过 json 格式进行返回。
总结,自此我们完成了对跨域问题相关知识的学习。
版权声明: 本文为 InfoQ 作者【梁龙先森】的原创文章。
原文链接:【http://xie.infoq.cn/article/555eda5d6ac8168877ee39977】。文章转载请联系作者。
评论