跨域的几种方法
浏览器出于安全方面的考虑,只允许客户端与本域(同协议、同域名、同端口,三者缺一不可)下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源,这被称为同源策略。
而有时候,我们不得不在一个客户端下访问不同域中的资源,于是需要用到一些方法来避开浏览器的同源策略,这些方法被称为跨域。
实现跨域有如下几种方法:
JSONP(JSON with Padding)是数据格式JSON的一种使用模式,可以使网页实现跨域请求。其原理主要利用了 HTML 的 script 标签。由于 script 是采用开放策略,通过设置 src 引入不同域下的资源,所以可以通过 script 实现跨域,该方法需要后端支持。jsonp跨域的实现步骤如下:
下面来做个演示,首先为演示方便,将系统的hosts做如下修改:
以上例子最终实现了由example.a.com到example.b.com的跨域。应注意的是,因为 script 只能发送GET请求,所以jsonp只能实现GET请求的跨域。如果希望能实现其他请求的跨域,就可以用接下来介绍的一种方法——CORS。
CORS(全称为:Cross-Origin Resouce Sharing)跨域资源共享,是一种通过ajax跨域请求资源的方法。浏览器将CORS请求分为两大类,简单请求(simple request)和非简单请求(not-so-simple request,浏览器对这两种请求的处理方式不一样。如果请求满足以下两个条件,则为简单请求。
简单请求的实现方式即当用XMLHttpRequest发请求时,浏览器如果发现该请求不符合同源策略,会给该请求加上一个请求头origin,origin用来说明本次请求来自哪个源(协议+域名+端口)。如果origin指定的源不在后台允许范围内,后台会返回一个正常的HTTP响应,然后浏览器会发现该响应头部信息不包含Access-Control-Allow-Origin字段,然后抛出一个错误,该错误被XMLHttpRequest的onerror函数捕获,响应被驳回,但因为该错误无法通过状态码识别,所以HTTP回应的状态码还是200。如果origin在后台允许范围内,则服务器返回的响应,会包含Access-Control-Allow-Origin:Origin(指定的源)信息,浏览器此时不会抛错,响应能正常处理。
非简单请求是是请求方法为PUT或DELETE,又或者Content-Type为application/json的对服务器有特殊要求的请求。非简单请求的CORS请求,会在正式通信前增加一次HTTP查询,称为预检(preflight),询问服务器当前网页所在域名是否在服务器的许可名单中,如果在,则发出正式的XMLHttpRequest,之后就与简单请求一样,不在则报错。
依旧用上面的例子。
最终实现的效果与第一个jsonp的例子一样。
还有一种方式,就是通过降域来实现跨域。即通过设置document.domain的方式,将两个域名的domain设置为一个,如对于a.example.com和b.example.com,可以通过js设置 document.domain = "example.com" ,实现跨域。
做个演示,假设在 下有一个a.html文件,其中a.html中有一个 iframe ,它的 src 为 。
用降域方法实现跨域操作简单,但是有一些缺点。比如域名只能往下设置,不能回去,比如从example.com回到a.example.com。同时如果一个子域名被攻击,多个被降域的域名都会被连带攻击,有很大的安全风险。
postMessage是一个web API,可以实现跨域通信。 window.postMessage() 被调用时,会在所有页面脚本执行完毕后,向目标窗口派发一个 MessageEvent 消息。语法如下:
MessageEvent 具有如下属性:
用一个与上面降域类似的例子来做演示。同样有两个页面a.html和b.html,a.html中的 iframe 的 src 指向b.html。
最终实现a.html与b.html通信效果如下:
使用postMessage方法应注意的是,如果不希望从其他网站接收message,那么不要为message事件添加任何监听器。如果确实希望接收其他网站的message,那么应该始终使用origin和source属性来验证发件人的身份,以免被恶意的网站攻击。
以上就是几种常见的跨域方法,各有优劣,且各自都有一定的安全问题,在日常应用中,需要有针对性的使用,对可能的安全风险采取相应措施。
前后端联调——跨域问题
19.7.24
前端通过http请求跨域的同时需要带上cookie信息,前端需要设置withCredentials = true。
而后端也需要有所修改。
Access-Control-Allow-Origin 字段必须指定域名,不能为*
Access-Control-Allow-Credentials为true
后端可以通过HtttpServletRequest的Header中找到Origin。是跨域地址的host加port。
后端需要维护一个跨域URL的白名单,用Origin contains 匹配白名单的URL,成功则配置response 的 Access-Control-Allow-Origin,指定Origin。
就实现跨域传cookie了。
参考:
什么是跨域、怎么解决跨域?
一个请求url的** 协议、端口、域名 **其中任意一个与当前页面url不相同就是跨域
即: (http/https)协议、(segmentfault)主域名、(www)子域名、(8080)端口
是因为浏览器的同源策略的限制,同源策略是一种安全策略,同源指的是域名,协议,端口相同,会阻止一个域的js脚本和另一个域的内容进行交互。防止在一个浏览器中的两个页面产生不安全、异常的行为。
当然如果不同源的话会产生一定的限制:
【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
【2】无法接触非同源网页的 DOM
【3】无法向非同源地址发送 AJAX 请求
document.createElement(‘script’) 生成一个 script 标签,然后插 body 里而已。
JSONP的实现原理就是创建一个script标签, 再把需要请求的api地址放到src里. 这个请求只能用GET方法, 不可能是POST(向服务端传送数据)。
一种非正式传输协议,它会允许用户传递一个callback参数给服务端,然后服务端返回数据的时候会将这个callback参数作为函数名来包裹住JSON数据,然后客户端就可以随意的定义自己的函数来处理返回的数据了。
一般是后端在处理请求数据的时候,添加允许跨域的请求头信息,服务端设置Access-Control-Allow-Origin就可以,如果需要携带cookie,前后端都需要设置
window对象有个name的属性,在一个window下,窗口载入的页面都是共享一个window.name。
在a.html中,怎么把b.html页面加载进来,获取b.html的数据。在a.html页面使用iframe,可以去获取b.html的数据,然后在a.html页面中取得iframe获取得数据。
但是iframe想要获取b.html中的数据,只需要给这个iframe的src设为就可以,如果a.html想要得到iframe所获得的数据,也就是iframe的window.name的值,还要把这个iframe的src设成跟a.html页面同一个域才可以,不然a.html访问不到iframe里的window.name属性。
// 父窗口打开一个子窗口
var openWindow = window.open('', 'title');
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', '');
调用message事件,监听对方发送的消息
// 监听 message 消息
window.addEventListener('message', function (e) {
console.log(e.source); // e.source 发送消息的窗口
console.log(e.origin); // e.origin 消息发向的网址
console.log(e.data); // e.data 发送的消息
},false);
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址
location ^~ /api {
proxy_pass ;
}
}
// 请求的时候直接用回前端这边的域名,这就不会跨域,然后Nginx监听到凡是localhost:9099/api这个样子的,都转发到真正的服务端地址
fetch('', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
msg: 'helloIframePost'
})
})
关于跨域的问题
一、在前端开发过程中,如果准备开发富应用,跨域的问题将会随之而来。
我们先看看什么是跨域呢:
所谓跨域,或者异源,是指主机名(域名)、协议、端口号只要有其一不同,就为不同的域(或源) 。出于保护用户数据的目的,浏览器有一个最基本的策略就是同源策略,只允许页面内的脚本访问当前域的资源(加载脚本、资源等不受此限制)。
二、如果浏览器厂商不对跨域请求进行处理,会给我们带来什么危害呢?
有心人士(病毒制造者)会利用这个漏洞进行如下攻击:
1. CSRF/XSRF 攻击 ,简单的来讲就是在 b.com 页面中请求 a.com 的接口(浏览器会自动带上 用户在 a.com 的 cookie),从而获取用户的在 a.com 的相关信息。
2. XSS 注入攻击 ,类似于 SQL 攻击,提交含有恶意脚本的数据到服务器,从而达到破坏页面或者获取用户的 cookie。
三、我们了解到了什么是跨域,那我们又应该如何解决呢,现在找到了这些比较权威的文章,大家先品读一下:
1. mozilla 官方网站关于跨域的文章(Cross Origin), HTTP访问控制(CORS)
2. mozilla 官方网站关于浏览器同源策略的简要介绍(Same Origin), 浏览器的同源策略
四、读完这些文章,你打算怎么处理跨域问题呢,我先谈谈自己关于跨域的解决方案:
1. 采用 CORS 协议,直接在 Nginx 中设置允许跨域的 header(也可以在后端的应用程序内设置,不过在 Nginx 入口配置的话更加统一),在 location 配置中直接使用指令 add_header( 官方文档链接 ),示例配置如下:
2. 使用 JSONP,也是需要后端配合,利用“浏览器加载脚本、资源时不受同源策略的约束”这个特性,但是这种方式非常受限制,例如只能使用 GET 请求,不能携带自定义 header 等。
3. 其他的一些方法,例如 window.name, document.domain 以及 HTML5 中的特性 window.postMessage 等
五、其他参考链接
1. 浅谈JS跨域问题
2. 跨域资源共享 CORS 详解----阮一峰
六、声明
现在网络上的知识非常复杂,有些是摘自权威书籍的,有些是作者自己理解然后记录下来的,有些是瞎掰的,所以一定要结合情况多多甄别,对于有权威文档的知识点,建议先参考文档。
后端配置跨域
原文连接: 原文地址
跨域的详细介绍可以参考: 浏览器和服务器实现跨域(CORS)判定的原理 ,这里不多赘述。
1、主要就是客户端向发送了服务端请求,服务器已经能返回数据,但是浏览器不接收
2、在接口里面加上:( 因为request是处理请求,response是返回结果 )
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control","no-cache");
3、如果是使用Spring Boot创建的项目,直接添加 一句注解 到controller和方法就可以了:
@CrossOrigin
其中@CrossOrigin中的2个参数:
origins : 允许可访问的域列表
maxAge :准备响应前的缓存持续的最大时间(以秒为单位)。
在这个例子中,对于retrieve()和remove()处理方法都启用了跨域支持,还可以看到如何使用@CrossOrigin属性定制CORS配置。如果同时使用controller和方法级别的CORS配置,Spring将合并两个注释属性以创建合并的CORS配置。
4、如果您正在使用Spring Security,请确保在Spring安全级别启用CORS,并允许它利用Spring MVC级别定义的配置。
二、全局CORS配置
除了细粒度、基于注释的配置之外,您还可能需要定义一些全局CORS配置。这类似于使用筛选器,但可以声明为Spring MVC并结合细粒度@CrossOrigin配置。默认情况下,所有origins and GET, HEAD and POST methods是允许的。
JavaConfig
使整个应用程序的CORS简化为:
更多使用请看原文连接和官方文档
后端解决前端跨域请求问题
场景:前后端分离,页面和后端项目部署在不同服务器,出现请求跨域问题。
原因:CORS:跨来源资源共享(CORS)是一份浏览器技术的规范,提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比JSONP要来的好,JSONP对于 RESTful 的 API 来说,发送 POST/PUT/DELET 请求将成为问题,不利于接口的统一。但另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。不过现代的浏览器(IE10以上)基本都支持 CORS。
预检请求(option):在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求(一般都是浏览检测到请求跨域时,会自动发起),以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。
解决方案:
1、创建一个过滤器,过滤options请求。
package com.biz.eisp.sci.util;
import org.apache.commons.httpclient.HttpStatus;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 解决跨域问题
*
*/
public class CorsFilterimplements Filter {//filter 接口的自定义实现
public void init(FilterConfig filterConfig)throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
if ("OPTIONS".equals(request.getMethod())){//这里通过判断请求的方法,判断此次是否是预检请求,如果是,立即返回一个204状态吗,标示,允许跨域;预检后,正式请求,这个方法参数就是我们设置的post了
response.setStatus(HttpStatus.SC_NO_CONTENT); //HttpStatus.SC_NO_CONTENT = 204
response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS, DELETE");//当判定为预检请求后,设定允许请求的方法
response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with"); //当判定为预检请求后,设定允许请求的头部类型
response.addHeader("Access-Control-Max-Age", "1"); // 预检有效保持时间
}
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
2、修改web.xml文件
filter
filter-namecors/filter-name
filter-classcom.biz.eisp.sci.util.CorsFilter/filter-class
/filter
filter-mapping
filter-namecors/filter-name
url-pattern/* /url-pattern
/filter-mapping
3、spring-mvc.xml添加HttpRequestHandlerAdapter http请求处理器适配器。
HttpRequestHandlerAdapter作为HTTP请求处理器适配器仅仅支持对HTTP请求处理器的适配。它简单的将HTTP请求对象和响应对象传递给HTTP请求处理器的实现,它并不需要返回值。它主要应用在基于HTTP的远程调用的实现上。
bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/