一、认识跨域
很多人误认为资源跨域时无法请求,通常情况下时可以正常发起的(部分浏览器存在特例),后端也进行了正常处理,只是在返回时被浏览器拦截,导致响应内容无法使用。可以论证这一点的著名案例就是CSRF跨站攻击。
CSRF跨站攻击
CSRF(Cross Site Request Forgery) 跨站请求伪造。也被称为One Click Attack和Session Riding,通常缩写为CSRF或XSRF。通常来说就是攻击者盗用你的身份,以你的名义发送恶意请求。
注意这是真实的攻击手段:
举个例子,假设你登录了邮箱,正常情况下可以通过某个链接http:xx.mail.com/send可以发送邮件。此时你又访问了别的网站,网站中有黄色广告,点击后广告会请求http:xx.mail.com/send。此时相当于在盗版网站中调用了发送邮件的链接,访问时会使用你邮箱网站的cookie信息。虽然盗版网站会提示跨域,但服务端任然进行了相应处理。
实际上,除了不同站点间存在跨域,相同站点之间也会存在跨域。而且这种都是浏览器的行为。
记得在人生的第一家公司上班时,公司有几个管理后台,子域名不同,比如时a.xx.com和b.xx.com。当时时在b.xx.com的某个详情页面中引用了a.xx.com的部分前端页面片段。代码写的比较早,刚开始没什么问题。直到有一天运营同学升级了google浏览器后,发现b.xx.com详情页中有些信息显示不出来了。排查发现就是跨域问题。很多人一应该都遇到过。
只要请求的URL与发送请求页面的URL的首部不同就会发生跨域:
- 比如http://a.xx.com下访问https://a.xx.com会跨域,协议不同。
- 在a.xx.com下访问b.xx.com会跨域,域名不同。
- a.xx.com:80下访问a.xx.com:8080会跨域,端口不同。
这个时候首部的含义就显而易见了:从协议开始到端口结束,只要url不同就被认为跨域,与域名对应的ip地址也不行。
浏览器解决跨域问题的方法有多种,包括JSONP、Nginx转发和CORS等。其中,JSONP和CORS需 要后端参与。
二、实现跨域-JSONP
JSONP(JSON With Padding)是一种非官方的解决方案。由于浏览器允许一些带src属性的标签跨域,例如,iframe、script、img等,所以JSONP利用script标签可以实现跨域。
这里不做详细介绍,因为我们有正规军:cors
三、实现跨域-CORS
CORS(Cross-Origin Resource Sharing)的规范中有一组新增的HTTP首部字段,允许服务器声明其 提供的资源允许哪些站点跨域使用。
通常情况下,跨域请求即便在不被支持的情况下,服务器也会接收并进行处理,在CORS的规范中则避免了这个问题。浏览器首先会发起一个请求方法为OPTIONS的预检请求,用于确认服务器是否允许跨域,只有在得到许可后才会发出实际请求。此外,预检请求还允许服务器通知浏览器跨域携带身份凭证(如cookie)。
CORS新增的HTTP首部字段由服务器控制,下面我们来看看常用的几个首部字段:
access-control-allow-origin
允许取值<origin>为或*。指被允许的站点,使用URL首部匹配原则。*匹配所有站点,表 示允许来自所有域的请求。
比如我们看看csdn的:
但并非所有情况都简单设置即可,如果需要浏览器在发起请求时携带凭证信息,则不允许设置为*。如果设置了具体的站点信息,则响应头中的Vary字段还需要携带Origin属性,这将告诉客户端:服务器对不同的源站返回不同的内容,然浏览器区分对待。
vary: Accept-Encoding, Origin
Access-Control-Allow-Methods
该字段仅在预检请求的响应中指定有效,用于表明服务器允许跨域的HTTP方法,多个方法之间用逗号隔开。
Access-Control-Allow-Headers
该字段仅在预检请求的响应中指定有效,用于表明服务器允许携带的 首部字段。多个首部字段之间用逗号隔开。
Access-Control-Max-Age
该字段用于指明本次预检请求的有效期,单位为秒。在有效期内,预检请求不需要再次发起。
Access-Control-Allow-Credentials
该字段取值为true时,浏览器会在接下来的真实请求中携带用户凭证信息(cookie等),服务器也可以使用Set-Cookie向用户浏览器写入新的cookie。注意,使用AccessControl-Allow-Credentials时,Access-Control-Allow-Origin不应该设置为*。
总的来说,cors时一种更安全的官方跨域解决方案,以来浏览器和服务端,当需要跨域的时候,通过服务端来限制可以跨域访问的url。
三、启用SpringSecurity的CORS支持
Spring Security对CORS提供了非常好的支持,只需在配置器中启用CORS支持,并编写一 个CORS配置源即可。
@Bean
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://www.baidu.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//对所有url生效
source.registerCorsConfiguration("/**", configuration);
return source;
}
核心实现在DefaultCorsProcessor中的handleInternal方法中。