为什么会有跨域问题
因为跨域问题是浏览器对于ajax请求的一种安全限制,一个页面发起的ajax请求,只能是与当前页相同域的url,这能有效的阻止跨站攻击。所以跨域问题只针对ajax请求,不包括静态资源的请求。
什么是跨域问题
当ajax请求和页面的域名不同、端口不同、协议不同时就会产生请求失败的情况。
协议不同可以是,http协议和https协议。
如果出现跨域问题,浏览器控制台会报这样一个错
解决方案
1、Jsonp
2、nginx反向代理
3、CORS方式
1、Jsonp
最早的解决方案,利用script标签可以跨域的原理实现。
优缺点:
https://www.cnblogs.com/ricesm/p/5055043.html
2、nginx反向代理
利用nginx把跨域反向代理为不跨域,支持各种请求方式。也就是在发送ajax请求时,把url写成和当前网页不跨域的形式,然后通过nginx反向代理到相应的web应用服务器路径。nginx作为中介。
优点:请求效率高,由于nginx支持的并发量很大,所以并不影响请求速度。后台程序无需修改。
缺点:需要在nginx进行额外配置,增加运维成本。
3、CORS方式
规范化的跨域请求解决方案,安全可靠。
优点:在服务端进行控制是否允许跨域,可自定义规则。支持各种请求方式。浏览器不需要做额外操作,正常请求即可。
缺点:如果是特殊请求,会产生额外的请求。不支持IE10以下版本浏览器。
介绍:
同源:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
XMLHttpRequest:http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
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:只限于三个值`application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain`
(三选一)
2.简单请求处理方案
发起一个ajax请求
请求信息如下:
ajax请求的域名是http://api.leyou.com
请求头
当浏览器发现发起的ajax请求是简单请求时,会在请求头中携带一个字段:`Origin`。
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条信息,就可以实现跨域。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class LeyouCorsConfiguration {
@Bean
public CorsFilter corsFilter(){
//初始化cors配置对象
CorsConfiguration configuration = new CorsConfiguration();
//允许这个域名跨域,可以设置多个,如果可以携带cookie则不能写*,*代表所有域名都能跨域
configuration.addAllowedOrigin("http://manage.leyou.com");
configuration.setAllowCredentials(true);//允许携带cookie
configuration.addAllowedMethod("*");//代表所有的请求方法都可以,(get post put delete等八种)
configuration.addAllowedHeader("*");//允许携带任何头信息
//初始化cors配置源对象
UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
configurationSource.registerCorsConfiguration("/**",configuration);//所有路径都要检测一下
//返回corsFilter实例,参数:cors配置源对象
return new CorsFilter(configurationSource);
}
}
我们只需要加上这样一个过滤器,就可以了。
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请求就无需再次进行预检了
如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。
代码实现
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class LeyouCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//1) 允许的域,不要写*,否则cookie就无法使用了
config.addAllowedOrigin("http://manage.leyou.com");
//2) 是否发送Cookie信息
config.setAllowCredentials(true);
//3) 允许的请求方式
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
// 4)允许的头信息
config.addAllowedHeader("*");
//2.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
原理和简单请求一样,只要把这个过滤器,放到spring容器就行了。