同源策略与跨域

造成跨域问题的原因就是浏览器的同源策略

所谓同源是指:域名、协议、端口均相同

跨域指:当前网站不能执行其他网站的JavaScript。

在一个浏览器的两个tab页中分别打开百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

同源策略是浏览器的行为,是浏览器为了保护本地数据不被JavaScript代码获取回来的数据污染而做出的拦截行为。即请求发送了,服务器响应了,但是无法被浏览器接收。

 

同源策略会使请求发出,并且服务器做出了响应,只是数据到了浏览器别丢弃了。

同源策略的限制内容有:

Cookie、LocalStorage、IndexedDB 等存储性内容

DOM节点

ajax跨域请求的数据

 

协议、域名、端口号不同时会造成跨域,所以前后端分离的项目,必然会跨域,即使部署在同一个服务器上,前后端的端口号肯定不同。

 

浏览器遵守同源策略,但是有些标签是允许跨域的。

<img>

<link>

<script>

所以jquery的js库可以引用cdn上的,因为<script>允许跨域。

 

how to 解决跨域?

最简单的两种:JSONP,CORS

 

Jsonp

Jsonp其实主要就是利用浏览器不去拦截<script>请求回来的数据。

<script>可以避免跨域问题,但他的内容一般都是js脚本,要么直接在<script>标签里面写脚本,要么网络请求js脚本。

在发送ajax请求时讲dataType换成jsonp。

jsonp处理跨域流程:

1,在发送跨域请求时,底层会随机生成一个方法名拼接在url上,比如callback=jQuery123...

2,得到callback方法名,拼接脚本+数据返回。

3,解析从后端获取的js脚本 执行jQuery123方法,最终把正确的值放入success中。

大致流程是:前端随机生成不可见的方法xxx -> 传递到后端,后端拼接JS脚本xxx(User) -> 前端得到JS脚本调用xxx(User) -> 最终调用到success:function(data){...},生成的xxx方法其实就是一个代理。

jsonp只支持get请求,因为其灵感来自流浪其允许<script>跨域。

cors(cross origin resource share)跨域资源共享

cors比jsonp更强大和灵活,需要浏览器和服务器的同时支持。

jsonp中需要后端配合返回js片段,在cors中浏览器会自动带上一些请求头,后端需要针对请求头做一些处理就好了。

 

简而言之,需要前后端进行协商,jsonp是前端js代码层面会带一些东西给后端(如回调方法名),cors方面是浏览器方面会带一些请求头给后端。

目前浏览器都支持该功能(会自动带上需要的请求头)IE10以上。cors不需要前端做任何事情,只需要后端配合就行。

 

CORS通信给ajax请求没有任何差别,因此我们不需要改变以前的业务逻辑,我们在后台只需要判断浏览器带过来的请求头能否跨域,然后在响应头中加入一些信息让浏览器知道是允许跨域的响应。一般用过滤器完成就行了。

 

cors通信过程简略图(简单请求)

 

 

方法或Controller类上加CrossOrigin(要跨域的域):在方法上表示该方法支持跨域,在类上表示类中所有方法支持跨域。

@Bean配置跨域Filter

/** * @author bravo * @date 2020-02-03 22:40 */ @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { //1.添加CORS配置信息 CorsConfiguration config = new CorsConfiguration(); //1) 允许的域,不要写*,否则cookie就无法使用了 config.addAllowedOrigin("http://localhost:7070"); //2) 是否发送Cookie信息 config.setAllowCredentials(true); //3) 允许的请求方式 config.addAllowedMethod("*"); // 4)允许的头信息 config.addAllowedHeader("*"); // 5) 有效时长 config.setMaxAge(3600L); //2.添加映射路径,我们拦截一切请求 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); //3.返回新的CorsFilter. return new CorsFilter(configSource); } }

WebMvcConfigurer添加跨域规则

/** * @author bravo * @date 2020-02-03 22:40 */ @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:7070") .allowCredentials(true) .allowedHeaders("*") // 允许的携带的头 .allowedMethods("*") // 允许的请求方式 .maxAge(3600L); } }

在上面几种方式的演进中,跨域规则被一步步抽取到“更高层次”的位置。如果你们公司恰好使用微服务,还可以把跨域规则提取到网关,也就是把Filter移到网关对所有微服务做统一跨域处理。

 

cors原理

加入我们在响应头没有添加CORS头,就认为后端不允许跨域,数据不可靠。所以只要后端返回浏览器需要的请求头,即可跨域(相应数据就不会被同源数据抛弃)

 

 

浏览器会把ajax请求分为两类,其处理方案略有差异:简单请求、特殊请求。

只要同时满足两大条件,就属于简单请求:

1.请求方法为HEAD、get、post

2.http的头信息不超过AcceptAccept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

当浏览器发现ajax请求是简单请求时,会在请求头携带一个字段Origin,CROS需要客户端服务端同时支持,这个动作相当于客户端的支持行为。

浏览器:Origin会指出当前请求属于哪个域(协议+域名+端口),服务器会根据这个值判断是否允许跨域。

服务端:响应头需要添加的信息

Access-Control-Allow-Origin: http://manage.leyou.com Access-Control-Allow-Credentials: true Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*(代表任意域名)

Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true

 

要想操作cookie,需要满足3个条件:

  • 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
  • 浏览器发起ajax需要指定withCredentials 为true
  • 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名

 

特殊请求:

非简单请求的请求,在与服务端进行正常通信之前会进行预检请求,增加一次http查询请求,会询问服务端允许跨域域名、一些http动词、头信息等。

一个“预检”请求的样板:

OPTIONS /cors HTTP/1.1 Origin: http://manage.leyou.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.leyou.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...

 

与简单请求相比,除了Origin以外,多了两个头:

  • Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
  • Access-Control-Request-Headers:会额外用到的头信息

 

预检请求的响应

服务的收到预检请求,如果许可跨域,会发出响应:

HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://manage.leyou.com Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Access-Control-Max-Age: 1728000 Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain

 

除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials以外,这里又额外多出3个头:

  • Access-Control-Allow-Methods:允许访问的方式
  • Access-Control-Allow-Headers:允许携带的头
  • Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了

 

如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值