目录
一、跨域
跨域问题是实际应用开发中一个非常常见的需求,在 Spring 框架中对于跨域问题的处理方案有好几种,引入了 Spring Security 之后,跨域问题的处理方案又增加了。
就不能在基于 传统的解决去解决跨域问题,因为默认的都会 Spring Security 的认证Filter 所拦截从而导致失效,因为 Spring 的三种解决方案两种是通过 Interceptor 拦截器而解决,而Spring Security 的 Filter 是在 Interceptor 拦截器前执行,及时是是通过 Spring 的 CorfFilter ,但是如果是没能在 Spring Security的 Filter 前执行也会导致失效。
二、什么是 CORS
CORS (Cross-Origin Resource Sharing ) 是由 W3C 制定的一种跨域资源共享技术标准,其目的就是为了解决前端的跨域请求。在 JavaEE 开发中,最常见的前端跨域请求解决方案是早期的 JSONP,但是 JSONP 只支持 GET 请求,这是一个很大的缺陷,而 CORS 则支持多种 HTTP 请求方法,也是目前主流的跨域解决方案 。
CORS 中新增了一组 HTTP 请求头字段,通过这些字段,服务器告诉浏览器,哪些网站通过浏览器有权限访问哪些资源。同时规定,对哪些可能修改服务器数据的 HTTP 请求方法(如 GET 以外的 HTTP 请求等),浏览器必现首先使用 OPTIONS 方法发起一个预检请求 (prenightst),预检请求的目的是查看服务端是否支持即将发起的跨域请求,如果服务端允许,才发送实际的 HTTP 请求 。在预检请求的返回中,服务器端也可通知客户端,是否需要携带身份凭证 (如 Cookie 、HTTP 认证信息等) 。
CORS :同源/同域 = 协议 + 主机 + 端口
2.1 简单请求
GET 请求为例,如果需要发起一个跨域请求,则请求头如下:
Host: localhost:8080
Origin: http://localhost:8081
Referer: http://localhost:8081/index.html
如果服务端支持该跨域请求,那么返回的响应头中将包含如下字段:
# 告知浏览器对该域的请求是允许跨域的
Access-Control-Allow-Origin:http://localhost: 8081
Access-Control-Allow-Origin 字段用来告诉浏览器可以访问该资源的域,当浏览器收到这样的响应头信息之后,提取出 Access-Control-Allow-Origin 字段中的值,发现该值包含当前页面所在的域,就知道这个跨域是被允许的,因此就不再对前端的跨域请求进行限制。这属于简单请求,既不需要进行预检请求的跨域。
2.2 非简单请求
对于一些非简单请求,会首发发送一个预检请求。预检请求类似下面这样:
OPTIONS /put HTTP/1.1 # 请求方式 和类型
Host: localhost:8080 # 请求的地址
Connection: keep-alive # 连接状态
Accept: */* # 允许的响应类型
Access-Control-Request-Method:PUT # 当前方法的请求方法
Origin: http://localhost: 8081 # 跨域请求的url
Referer:http://localhost:8081/index.html # 跨域的地址
请求方法是 OPTIONS ,请求头 Origin 就告诉服务端当前页面所在域,请求头 Access-Control-Request-Methods 告诉服务端即将发起的跨域请求所使用的方法。服务端对此进行判断,如果允许即将发起的跨域请求,则会给出如下响应:
HTTP/1.1 200 # 当前请求装填
Access-Control-Allow-Origin: htpp://localhost:8081 #浏览器允许跨域地址
Access-Control-Request-Methods:PUT # 请求方式
Access-Control-Max-Age:3600 # 预检请求的有效期
Access-Control-Allow-Methods 字段表示允许跨域方法:Access-Control-Max-Age 字段表示预检请求的有效期,单位为秒,在有效期内如果发起该跨域请求,则不用再次发起预检请求。预检请求结束后,接下来就会发起一个真正的跨域请求,跨域请求和前面的简单请求跨域步骤类似。
三、Spring 跨域解决方案
spring 跨域 Cors 解决方案_千城丶Y的博客-CSDN博客
四、Spring Security 跨域解决方案
4.1 原理分析
当我们为项目添加了 Spring Security 依赖之后,发现上面三种跨域方式有的失效了,有的则可以继续使用,这是怎么回事?
通过 @CrossOrigin 注解或者重写 addCorsMappings 方法配置跨域,统统失效了,通过CorsFilter 配置的跨域,有没有失效则要看过滤器的优先级,如果过滤器优先级高于 Spring Security 过滤器,即限于 Spring Security 过滤器执行,则 CorsFilter 所配置的跨域处理依然有效,如果过滤器优先级低于 Spring Security 过滤器,则 CorsFilter 所配置的跨域处理就会失效。
为了理清楚这个问题,我们先简单了解一下 Filter 、DispatcherServlet 以及 Interceptor 执行顺序。
理清楚了执行顺序,我们再来看跨域请求过程。由于非简单请求要首先发送一个预检请求 request ,而预检请求并不会携带认证信息,所以预检请求就有被 Spring Security 拦截的可能。因此通过 @CrossOrigin 注解或者重写addCorsMappings 方法配置跨域就会失效。如果使用 CorsFilter 配置的跨域,只要过滤器优先级高于 Spring Security 过滤器就不会有问题,反之同样出现问题。
4.1 解决方案
Spring Secuirty 中提供了更专业的方式来解决预检请求所面临的问题。
Spring Security 的解决方案是基于 SpringMvc 的 CorsFilter 实现的,也就是将CorsFilter的配置交给我们,而具体CorsFilter 类的创建交给 Spring Security 进行处理。
如:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* web安全配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.cors().configurationSource(corsConfiguration()); // 配置 CorsFilter 配置信息
}
/**
* Cors 的配置信息 配置+路径
*/
CorsConfigurationSource corsConfiguration (){
// Cors配置类
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(false); // 是否返回时生成凭证
corsConfiguration.setAllowedHeaders(Arrays.asList("*")); // 允许请求携带哪些请求头信息
corsConfiguration.setAllowedMethods(Arrays.asList("*")); // 允许哪些类型的请求方法
corsConfiguration.setAllowedOrigins(Arrays.asList("*")); // 允许哪些域可以进行方法
corsConfiguration.setMaxAge(3600L); // 设置预检的最大的时长
corsConfiguration.setExposedHeaders(Collections.emptyList()); // 设置返回暴露的响应头信息
// 设置注册URL 配置类
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource() ;
source.registerCorsConfiguration("/**",corsConfiguration);
return source;
}
}
4.3 源码分析
底层还是通过CorsFilter 处理 ,生成配置 CorsFilter 是交给 CorsConfigurer进行处理的。
结论:
Spring Security 底层也是使用 CorsFilter ,而这个Filter 执行顺序就至关重要,Spring Security 底层帮我我们进行排序,在其他的Filter 前执行就可以正常执行解决跨域问题了。
1.获取CorsFilter 添加到Spring Security Filter china 中
2.添加方法
3.添加后的顺序
4.Filter排序