一、介绍
最近遇到和兄弟部门联调服务时,反馈跨域问题,不知道什么原因,此处特做记录。
二、什么是同源策略?
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到 XSS、 CSFR 等攻击。
同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。
同源政策的目的:是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:
在京东网站上登陆了账号后,那么又去访问另一个网站,那么你现在的京东账号是登录的状态,并没有退出,然而这个网站是可以看到你在京东网站上登陆的信息和存的一些Cookie,会发生什么?
- 很显然,如果 Cookie 包含隐私,这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,同源政策是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
三、什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
跨域问题来源根据上面说的的同源策略,即只有 协议+域名+端口号相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题,比如a标签、script标签、甚至form标签可以直接跨域发送数据并接收数据
四、Springboot跨域支持
CORS是跨源资源分享。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。
以下代码是Springboot1.5中的配置,亲测可用。
注意:之前配置一直不生效,后来排查是因为有人配置了一个过滤器导致我下面的配置失效了。
另外,跨域配置后要测试,之前没有正确的测试方式,导致只能硬着头皮上,所以有正确的测试方式很重要,第五点贴了测试方式。
package com.xxx.api.open.config;
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;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @author: chenjiawang
* @CreateDate: 2019/3/19 15:39
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true); //sessionid 多次访问一致
// 允许访问的客户端域名
corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
corsConfiguration.addAllowedHeader("*"); // 允许任何头
corsConfiguration.addAllowedMethod("*"); // 允许任何方法(post、get等)
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 对接口配置跨域设置
return new CorsFilter(source);
}
}
五、跨域功能测试
有时候,我们后端支持跨域之后,想测试是否生效,可用打开个相同协议的页面(如都是http的),然后F12,在console中输入如下代码,按回车,只要不出现跨域报错就OK了。
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://10.5.130.12:9507/api/open/alarm/query/queryQkeAlarm');
xhr.setRequestHeader("usertoken","ccfe5");
xhr.send(null);
xhr.onload = function(e) {
var xhr = e.target;
console.log(xhr.responseText);
}
如下说明服务端支持跨域了:
一般跨域不支持的报错:
以下是因为协议不一样,协议不一样也是跨域。
以下是因为usertoken这个请求头不被后端允许:
此种报错,只需要在后端配置允许的请求头即可。解决方式:
六、ngnix跨域支持
没有试验过,记录下
七、前端支持:避免发生跨域
没有试验过。记录下
使用jsonp,由于jsonp请求是通过script的方式发送的(只有xhr的请求方式才有可能产生跨域问题),所以不会产生跨域问题。Spring MVC实现过程:前台使用ajax的get请求,将dataType设为“jsonp”;服务器创建一个类并继承抽象类AbstractJsonReponseBodyAdvice,最后注解@ControllerAdivece。使用jsonp的弊端,只能使用get方式请求,服务器需要改动代码,发送的不是xhr请求。