跨域请求问题及SpringBoot项目跨域配置
简介
什么是跨域请求
在 HTML 中,
<a>
,<form>
,<img>
,<script>
,<iframe>
,<link>
等标签以及 Ajax 都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样。若协议 + 域名 + 端口号均相同,那么就是同域。
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html | 失败 | 域名不同 |
跨域请求的隐患:CSRF 攻击
CSRF(Cross-site request forgery),跨站请求伪造。比如有两个网站,其中A网站是真实受信任的网站,而B网站是危险网站。在用户登陆了受信任的A网站是,本地会存储A网站相关的 Cookie,并且浏览器也维护这一个 Session 会话。这时,如果用户在没有登出A网站的情况下访问危险网站B,危险网站B模拟发出一个对A网站的请求(跨域请求),由于 Cookie 存在,危险网站就可以使用用户的身份的对A网站进行操作,而在A网站的角度来看是并不知道请求是由B网站发出来的(Session和Cookie均为A网站的)。
因而 CSRF 攻击可以简单理解为:攻击者盗用了你的身份,以你的名义发送请求。
针对 CSRF 的安全策略:同源策略
同源策略(Same-origin Policy)是 Netscape 提出的一个著名的安全策略,是浏览器最核心最基础的安全策略,现在所有的可支持 Javascript 的浏览器都会使用这个策略。
具体的同源策略
- DOM 层面的同源策略:限制了来自不同源的”Document”对象或 JS 脚本,对当前“document”对象的读取或设置某些属性
- Cookie 和 XMLHttprequest 层面的同源策略:禁止 Ajax 直接发起跨域HTTP请求(其实可以发送请求,结果被浏览器拦截,不展示),同时 Ajax 请求不能携带与本网站不同源的 Cookie。
- 同源策略的非绝对性:
<script><img><iframe><link><video><audio>
等带有 src 属性的标签可以从不同的域加载和执行资源。 - 其他插件的同源策略:
flash、java applet、silverlight、googlegears
等浏览器加载的第三方插件也有各自的同源策略,只是这些同源策略不属于浏览器原生的同源策略,如果有漏洞则可能被黑客利用,从而留下XSS攻击的后患
网页 f12 打开开发者模式可以在 console 中看到被拒绝的跨域请求,比如:
跨域解决办法1:CORS
跨源资源共享 Cross-Origin Resource Sharing(CORS) 是一个新的 W3C 标准,它新增的一组HTTP首部字段,允许服务端其声明哪些源站有权限访问哪些资源。换言之,它允许浏览器向声明了 CORS 的跨域服务器,发出 XMLHttpRequest 请求,从而克服 Ajax 只能同源使用的限制。
CORS 请求
CORS 请求是包含 Origin
首部的 HTTP 请求。(Origin 首部表明预检请求或实际请求的源站,值为源站 URI,不包含任何路径信息,只是服务器名称)
**对于 CORS 请求,浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。**在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
CORS 请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。
预检请求
预检请求用于检查服务端是否允许跨域请求,预检请求会使用OPTIONS
作为方法,并包含以下 header:
- Access-Control-Request-Method:告知服务器实际请求的方法
- Access-Control-Request-Headers:告知服务器实际请求将要携带的 Header
预检请求的重定向
大多数浏览器不支持针对于预检请求的重定向。如果一个预检请求发生了重定向,浏览器将报告错误。
HTTP 响应 Header
对于 CORS 请求的 HTTP 响应包含以下 Header,表明该服务器具体支持怎样的跨域请求:
- Access-Control-Allow-Origin:允许访问该资源的外域 URI,可使用 “*” 表示允许所有域的请求;
- Access-Control-Allow-Credentials:指定是否允许实际请求使用 credentials(身份凭证)
- Access-Control-Allow-Methods:允许的方法
- Access-Control-Allow-Headers:允许的 Header
- Access-Control-Max-Age:预检请求的结果能缓存的时间(单位:秒)
- Access-Control-Expost-Headers:服务器允许浏览器访问的 Header
因此,只要在服务端配置了相应的参数,CORS 请求就能顺利跨域访问了。
SpringBoot 配置跨域请求
CorsWebFilter
SpringBoot 中,可以在配置文件中配置一个 CorsWebFilter 的 Bean 来设置允许的跨域请求,来看这个类的构造函数:
public class CorsWebFilter implements WebFilter {
public CorsWebFilter(CorsConfigurationSource configSource) {
this(configSource, new DefaultCorsProcessor());
}
CorsConfigurationSource
CorsWebFilter 构造函数中需要传入CorsConfigurationSource
,该接口有一个实现类:UrlBasedCorsConfigurationSource
,这个实现类根据 CorsConfiguration
配置的属性,将这些规则映射到对应的路径上。
使用registerCorsConfiguration
方法将路径与 CorsConfiguration 实例指定的配置相映射:
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
CorsConfiguration
CorsConfiguration 类可以 setter 方法设置所有上面提到的关于 CORS 的 HTTP Response Header:
完整的配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource
= new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(urlBasedCorsConfigurationSource);
}
}
参考链接
https://blog.csdn.net/ppxin/article/details/94717173
https://www.jianshu.com/p/f880878c1398
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS