前提提示:最近的项目中,采用了前后端分离的开发模式。前端通过fetch api访问后端rest api。其间很自然的就会出现跨域问题。
什么是跨域?
以下是脑补之后的一些理解。
CORS(Cross-origin request sharing)跨域资源共享,是一个w3c标准,它允许你向一个不同源服务发出XMLHttpRequest请求,从而克服ajax只能请求同源服务的限制。
浏览器出于安全考虑,实施的一种同源策略,对非同源访问会主动拦截掉。
同源是指具有相同的:url协议(eg:http,https)、域名(eg:google.com)、端口
在浏览器中,script、img、iframe、link(这些标签似乎都有src属性)等标签可以加载跨域资源,而不受同源限制。
那么如何处理跨域的请求呢?目前比较多做法是jsonp,nginx反向代理,cors本文主要介绍cors.
jsonp:通过可以跨域的script标签,这样请求就能跨域访问了,通过约定好回调,就能获得相关响应数据。缺点是只支持GET不支持POST等其它类型请求;
nginx代理:通过一个代理服务器,转发请求到后端服务。缺点是扩展性不好。开发,测试,预发布等几个环境切换都需要配置。
cors: 支持所有类型的http请求,在服务端设置即可。缺点是老的浏览器不兼容(IE6,7,8 没关系,这些老版本不要管了)
CORS基本过程:
在HttpServertResponse响应中添加响应头:
Access-Control-Allow-Origin:(必设)添加浏览器允许的域名,可设置*表示任何。(origin是你浏览器上的地址组成的)
Access-Control-Allow-Credentials:(可设)是否允许发送cookie。如果设置为true,客户端请求也要将withCredentials设为true;
Access-Control-Request-Method: 必须的,表示CORS上会使用到那些HTTP方法;
Access-Control-Request-Headers: 必须的,表示CORS上会有那些额外的的有信息;
SpringBoot支持cors
SpringBoot中已经很好的支持了cors
CorsFilter类提供了改功能,通过构造UrlBasedCorsConfigurationSource类相关配置
以下是基本的代码:
@Configuration
public classCorsConfig {
private CorsConfiguration buildConfig(){
CorsConfiguration corsConfiguration =newCorsConfiguration();
corsConfiguration.addAllowedOrigin("*");//允许任何域名
corsConfiguration.addAllowedHeader("*");//允许任何头
corsConfiguration.addAllowedMethod("*");//允许任何方法get,post..
returncorsConfiguration;
}
@Bean
public CorsFiltercors Filter(){
UrlBasedCorsConfigurationSource source =new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",buildConfig());
return newCorsFilter(source);
}}
至此,一个接受任何域的跨域请求就ok了。
跨域共享cookie
但是还未结束,对于权限验证,目前还是使用的基于session,cookie的验证,所以这种方案需要实现跨域共享cookie.(强烈不建议使用cookie),那么对于客户端的请求就需要额外加上几个参数。
对于跨域共享cookie,需要注意的是后端响应的response head里的 Access-Control-Allow-Origin不能是通配符,且不能配置多个,Access-Control-Allow-Credentials需要设置为true
对于Access-Control-Allow-Origin的限制,只能指定单一域名或使用req.getHeader("Origin")来获取每次请求的Origin。这样一来,你要另外写一个Filter。
客户端的设置:以jQuery为例需要在里面加入xhrFields: {withCredentials: true},crossDomain: true
在这些修改做好后,访问后端接口,但是依然不能解决。排查了半天,是跨域请求之前都要执行一个OPTIONS请求,这个请求是一个预检测的服务,用于获知服务端能接受哪些请求方法,origin等信息。后端会在响应中返回一个cookie,在下一次请求时会带上这个cookie。就实现了基本的跨域cookie共享,后端也能正常获取到session。
这个请求可以特殊处理。