跨域访问
在日常开发中,尤其是在微服务架构下,不同的微服务部署在不同的端口上,不同端口之间的请求跳转有可能会遇到CORS问题,即:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
这是浏览器为了保证安全提供安全策略:
- 规定只有同源的网页才能共享Cookie
- 规定AJAX请求只能发给同源的网址,否则就报错
如果违反了以上规定,浏览器将会拦截跨域请求,抛出:**No ‘Access-Control-Allow-Origin’ header is present on the requested resource.**等提示。
解析我们从同源策略开始熟悉跨域访问以及跨域问题的解决。
1. 跨域问题由来
1.1 同源策略
1、同源策略简介
浏览器安全的基石是"同源政策"(same-origin policy)。
1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
-
协议相同
-
域名相同
-
端口相同
举例来说,http://www.example.com/dir/page.html
这个网址,协议是http://
,域名是www.example.com
,端口是80
(默认端口可以省略)。它的同源情况如下。
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
2、同源策略目的
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送。
3、跨域问题
虽然同源策略是必要的,但有时候合法的用途也会受到限制,这时我们可以通过三种方式进行规避:JSONP、WebSocket、CORS。
而作为后台开发,我们最常用的便是使用CORS。
1.2 CORS
1、CORS简介
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。
它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。
2、CORS执行流程
CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
我们暂且不谈,主要了解非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
1、浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的
XMLHttpRequest
请求,否则就报错。2、服务器收到"预检"请求以后,检查了
Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,确认允许跨源请求,就可以做出回应。3、一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个
Origin
头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin
头信息字段。
2.解决跨域问题
从1.2CORS我们知道:浏览器遇到跨域的AJAX请求时,先发一个预检请求询问服务器,当前网页所在的域名是否在服务器的许可名单之中,而作为服务器端的我们就收到预检请求之后,进行检查,如果符合就需要响应肯定回复。之后每次浏览器正常的CORS请求和普通请求一样。
1、浏览器端的预检请求
OPTIONS /cors HTTP/1.1 # 预检请求用的方法是OPTIONS
Origin: http://api.bob.com # 表示请求来自那个源
Access-Control-Request-Method: PUT # 浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Headers: X-Custom-Header # 指定浏览器CORS请求会额外发送的头信息字段
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
2、服务端对浏览器的预检请求的肯定响应
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://api.bob.com # 表示该网址可以请求数据,*表示允许跨源请求
Access-Control-Allow-Methods: GET, POST, PUT # 表明服务器支持的所有跨域请求的方法
Access-Control-Allow-Headers: X-Custom-Header # 表明服务器支持的所有头信息字段
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
3、浏览器正常请求
PUT /cors HTTP/1.1
Origin: http://api.bob.com # 浏览器自动添加
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
4、服务器正常恢复
Access-Control-Allow-Origin: http://api.bob.com # 每次回应都必定包含的
Content-Type: text/html; charset=utf-8
1.1 SpringCloud环境下解决同源问题
从上面可以知道,作为服务器端,我们需要判断Origin: http://api.bob.com
、Access-Control-Request-Method
等判断该请求是否合法跨源请求,如果合法即响应Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等来确认。
在微服务中,我们在网关对请求进行拦截,过滤出跨域请求,并允许所有跨域请求:
@Configuration
public class ApplicationCorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许所有的请求头
corsConfiguration.addAllowedHeader("*");
// 允许所有方法跨域
corsConfiguration.addAllowedMethod("*");
// 运行所有源请求跨域
corsConfiguration.addAllowedOrigin("*");
// 跨域请求默认不包含Cookie,设置为true可以包含Cookie
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}
3. 总结
1、Netscape公司为了保证浏览器的使用安全引用了同源策略,同源策略中规定Ajax请求只能发给同源网址,否则报错。
2、但是在实际场景下有可能需要用到跨源访问,于是W3C提出了CORS标准,允许我们通过特殊策略,进行跨源资源分享。
3、CORS标准下,浏览器会对跨源请求发送预检请求,作为服务器端需要判断请求是否合法,同时响应预检请求。