为什么会有跨域问题
因为跨域问题是浏览器对于ajax请求的一种安全限制,一个页面发起的ajax请求,只能是与当前页相同域的url,这能有效的阻止
跨站攻击。所以跨域问题只针对ajax请求,不包括静态资源的请求。
什么是跨域问题
当ajax请求和页面的
域名不同、端口不同、协议不同时就会产生请求失败的情况。
协议不同可以是,http协议和https协议。
如果出现跨域问题,浏览器控制台会报这样一个错
解决方案
1、Jsonp
2、nginx反向代理
3、CORS方式
1、Jsonp
最早的解决方案,利用script标签可以跨域的原理实现。
2、nginx反向代理
利用nginx把跨域反向代理为不跨域,支持各种请求方式。也就是在发送ajax请求时,把url写成和当前网页不跨域的形式,然后通过nginx反向代理到相应的web应用服务器路径。nginx作为中介。
优点:请求效率高,由于nginx支持的并发量很大,所以并不影响请求速度。后台程序无需修改。
缺点:需要在nginx进行额外配置,增加运维成本。
3、CORS方式
规范化的跨域请求解决方案,安全可靠。
优点:在服务端进行控制是否允许跨域,可自定义规则。支持各种请求方式。浏览器不需要做额外操作,正常请求即可。
**缺点:**如果是特殊请求,会产生额外的请求。不支持IE10以下版本浏览器。
介绍:
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
CORS通信与AJAX没有任何差别,因此你不需要改变以前的业务逻辑。只不过,浏览器会在请求中携带一些头信息,我们需要以此判断是否允许其跨域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可。
原理:
1.浏览器会将ajax请求分为两类,其处理方案略有差异:简单请求、特殊请求。
一、**简单请求(
重要)**
1.什么是简单请求?
** 简单请求**是满足以下两种情况的请求:
a. 请求方法为
head、get、post
** ** b. http的
头信息超出一下几种字段:
– Accept
– Accept-Language
– Content-Language
– Last-Event-ID
– Content-Type:只限于三个值
1 | 1` |
application/x-www-form-urlencoded
1 | 1` |
、
1 | 1` |
multipart/form-data
1 | 1` |
、
1 | 1` |
text/plain
1 | 1` |
(三选一)
2.简单请求处理方案
发起一个ajax请求
请求信息如下:
ajax请求的域名是http://api.leyou.com
请求头
当浏览器发现发起的ajax请求是简单请求时,会在请求头中携带一个字段:
1 | 1` |
Origin
1 | 1` |
。
Origin中会指出当
前请求属于哪个域(协议+域名+端口,形如 Origin : http://manage.leyou.com)。服务器会根据这个值决定是否允许其跨域。
origin就是当前页面的
域名+端口+协议
我们可以看出,ajax请求的域名和当前域名不一致,存在跨域问题
响应头
如果服务器
允许跨域,需要在返回的响应头中携带下面信息:
Access-Control-Allow-Origin: http://manage.leyou.com
作用是告诉浏览器,该请求允许跨域的白名单。可接受的域,是一个具体域名或者 * , * 就是任意域名。也就是说服务端允许该ajax请求在http://manage.leyou.com 这个域名下跨域
Access-Control-Allow-Credentials: true
是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true
Content-Type: text/html; charset=utf-8
小结
有了携带
这三条信息 的响应头,浏览器 就会同意该ajax请求跨域了。
代码实现
原理已经清楚了,就是
服务端需要在响应头给加3条信息,就可以实现跨域。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.web.cors.CorsConfiguration;
4import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
5import org.springframework.web.filter.CorsFilter;
6@Configuration
7public class LeyouCorsConfiguration {
8 @Bean
9 public CorsFilter corsFilter(){
10 //初始化cors配置对象
11 CorsConfiguration configuration = new CorsConfiguration();
12 //允许这个域名跨域,可以设置多个,如果可以携带cookie则不能写*,*代表所有域名都能跨域
13 configuration.addAllowedOrigin("http://manage.leyou.com");
14 configuration.setAllowCredentials(true);//允许携带cookie
15 configuration.addAllowedMethod("*");//代表所有的请求方法都可以,(get post put delete等八种)
16 configuration.addAllowedHeader("*");//允许携带任何头信息
17 //初始化cors配置源对象
18 UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
19 configurationSource.registerCorsConfiguration("/**",configuration);//所有路径都要检测一下
20 //返回corsFilter实例,参数:cors配置源对象
21 return new CorsFilter(configurationSource);
22 }
23}
24
我们只需要加上这样一个过滤器,就可以了。
CorsFilter是spring给我们准备好的。我们需要加点配置就行了。
**二、特殊请求(
不重要)**
不符合简单请求的条件,会被浏览器判定为特殊请求,例如请求方式为PUT。
**预检请求 **
特殊请求会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
一个“预检”请求的样板:
与
简单请求相比,除了Origin以外,多了
两个头:
Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
Access-Control-Request-Headers:会额外用到的头信息
**预检请求的响应 **
服务的收到预检请求,如果许可跨域,会发出响应:
除了
Access-Control-Allow-Origin和
Access-Control-Allow-Credentials以外,这里又额外多出3个头:
Access-Control-Allow-Methods:允许访问的方式
Access-Control-Allow-Headers:允许携带的头
Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了
如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.web.cors.CorsConfiguration;
4import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
5import org.springframework.web.filter.CorsFilter;
6
7@Configuration
8public class LeyouCorsConfig {
9 @Bean
10 public CorsFilter corsFilter() {
11 //1.添加CORS配置信息
12 CorsConfiguration config = new CorsConfiguration();
13 //1) 允许的域,不要写*,否则cookie就无法使用了
14 config.addAllowedOrigin("http://manage.leyou.com");
15 //2) 是否发送Cookie信息
16 config.setAllowCredentials(true);
17 //3) 允许的请求方式
18 config.addAllowedMethod("OPTIONS");
19 config.addAllowedMethod("HEAD");
20 config.addAllowedMethod("GET");
21 config.addAllowedMethod("PUT");
22 config.addAllowedMethod("POST");
23 config.addAllowedMethod("DELETE");
24 config.addAllowedMethod("PATCH");
25 // 4)允许的头信息
26 config.addAllowedHeader("*");
27
28 //2.添加映射路径,我们拦截一切请求
29 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
30 configSource.registerCorsConfiguration("/**", config);
31
32 //3.返回新的CorsFilter.
33 return new CorsFilter(configSource);
34 }
35}
36
原理和简单请求一样,只要把这个过滤器,放到spring容器就行了。