跨域问题是实际应用开发中一个非常常见的需求,在spring框架中对于跨域问题的处理方案有好几种,引入了spring security之后,跨域问题的处理方案又增加了。
1.什么是cors
cors(cross-origin resource sharing)是由w3c制定的一种跨域资源共享技术标准,其目的就是为了解决前端的跨域请求。在javaee开发中,最常见的前端跨域请求解决方案是早起的jsonp,但是jsonp只支持get请求,这是一个很大的缺陷,而cors则支持多种http请求方法,也是目前主流的跨域解决方案。
cors中新增了一组http请求头字段,通过这些字段,服务器高炉浏览器,哪些网站通过浏览器有权限访问哪些资源。同时规定,对那些可能修改服务器数据的http请求方法(如get以外的http请求等),浏览器必须首先使用options方法发起一个预检请求(prenightst),预检请求的目的是查看服务端是否支持即将发起的跨域请求,如果服务端允许,才发送实际的http请求。在预检请求的返回中,服务端也可以通知客户端,是否需要携带身份凭证(如cookies、http认证信息等)。
cors: 同源/同域 = 协议 + 主机 + 端口
2.简单请求
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字段中的值,发现该值包含当前页面所在的域,就知道这个跨域是被允许的,因此就不再对前端的跨域请求进行限制。这属于简单请求,即不需要进行预检请求的跨域。
3.非简单请求
对于一些非简单请求,会首先发送一个预检请求。预检请求类似下面这样:
options /put http/1.1 host: localhost:8080 connection: keep-alive accept: */* access-control-request-method: put origin: http://localhost:8081 referer: http://localhost:8081/index.html
请求方法是options,请求头origin就钙素服务端当前页面所在域,请求头access-control-request-methods告诉服务器端即将发起的跨域请求所使用的方法。服务端对此进行判断,如果允许即将发起的跨域请求,则会给出如下响应:
http/1.1 200 access-control-allow-origin:http://localhost: 8081 access-control-request-methods: put access-control-max-age: 3600
access-control-allow-methods字段表示允许的跨域方法:access-control-max-age字段表示预检请求的有效期,单位为秒,在有效期内如果发起该跨域请求,则不用再次发起预检请求。预检请求结束后,接下来就会发起一个真正的跨域请求,跨域请求和前面的简单请求跨域步骤类似。
4.spring跨域解决方案
4.1.@crossorigin
spring中第一种处理跨域的方式是通过@crossorigin注解来标记支持跨域,该注解可以添加在方法上,也可以添加在controller上。当添加在controller上时,表示controller中的所有接口都支持跨域,具体配置如下:
@restcontroller
public class indexcontroller {
@getmapping("/hello")
@crossorigin(origins = "http://localhost:8081")
public string hello(){
system.out.println("hello ok");
return "hello ok";
}
}@crossorigin注解各属性含义如下:
- allowcredentials:浏览器是否应当发送凭证信息,如cookie。
- allowedheaders:请求被允许的请求头字段,*表示所有字段。
- exposedheaders:哪些响应头可以作为相应的一部分暴露出来。
注意,这里只可以一一列举,通配符 * 在这里是无效的。
- maxage:预检请求的有效期,有效期内不必再次发送预检请求,默认是1800秒
- methods:允许的请求方法,*表示允许的所有方法。
- origins:允许的域,*表示允许所有域。
4.2.addcrossmapping自定义mvc配置
@crossorigin注解需要添加在不同的controller上。所以还有一种全局配置方法,就是通过重写webmvcconfigurercomposite#addcorsmappings方法来实现,具体配置如下:
//自定义mvc配置类
@configuration
public class webmvcconfig implements webmvcconfigurer {
//用来全局处理跨域
@override
public void addcorsmappings(corsregistry registry) {
registry.addmapping("/**") //处理的请求地址
.allowedmethods("*")
.allowedorigins("*")
.allowedheaders("*")
.allowcredentials(false)
.exposedheaders("")
.maxage(3600);
}
}4.3.corsfilter
corsfilter是spring web中提供的一个处理跨域的过滤器,开发者也可以通过该过滤器处理跨域。
import org.springframework.boot.web.servlet.filterregistrationbean;
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 org.springframework.web.servlet.config.annotation.corsregistry;
import org.springframework.web.servlet.config.annotation.webmvcconfigurer;
import java.util.arrays;
/**
* @author zhangkaitao
* @date 2022/7/24 10:30
*/
//自定义mvc配置类
@configuration
public class webmvcconfig implements webmvcconfigurer {
@bean
filterregistrationbean<corsfilter> corsfilter(){
filterregistrationbean<corsfilter> registrationbean = new filterregistrationbean<>();
corsconfiguration corsconfiguration = new corsconfiguration();
corsconfiguration.setallowedheaders(arrays.aslist("*"));
corsconfiguration.setallowedmethods(arrays.aslist("*"));
corsconfiguration.setallowedorigins(arrays.aslist("*"));
corsconfiguration.setmaxage(3600l);
urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource();
source.registercorsconfiguration("/**",corsconfiguration);
registrationbean.setfilter(new corsfilter(source));
registrationbean.setorder(-1);
return registrationbean;
}
}5.springsecurity跨域解决方案
当我们为项目添加了spring security依赖后,发现上面三种跨域方式有的失效了,有的则可以继续使用,这是怎么回事?
通过@crossorigin注解或者重写addcorsmappings方法配置跨域,统统失效了,通过corsfilter配置的跨域,有没有失效则要看过滤器的优先级,如果过滤器优先级高于springsecurity过滤器,即先于spring security过滤器执行,则corsfilter所配置的跨域处理依然有效;如果过滤器优先级地域spring security过滤器,则corsfilyer所配置的跨域处理就会失效。
以下是filter、dispatcherservlet以及interceptor执行顺序。

由于非简单请求都要首先发送一个预检请求(request),而预检请求并不会携带认证信息,所以预检请求就有被spring security拦截的可能。因此通过@crossorigin注解或者重写addcorsmappings方法配置跨域就会失效。如果使用corsfilter配置的跨域,只要过滤器优先级高于springsecurity过滤器就不会有问题,反之同样会出现问题。

@configuration
public class securityconfig extends websecurityconfigureradapter {
//...
@override
protected void configure(httpsecurity http) throws exception {
http.authorizehttprequests()
.anyrequest().authenticated()
.and()
.formlogin()
.and()
.cors().configurationsource(corsconfigurationsource())
.and()
.csrf().disable();//开启csrf
}
corsconfigurationsource corsconfigurationsource(){
corsconfiguration corsconfiguration = new corsconfiguration();
corsconfiguration.setallowedheaders(arrays.aslist("*"));
corsconfiguration.setallowedmethods(arrays.aslist("*"));
corsconfiguration.setallowedorigins(arrays.aslist("*"));
corsconfiguration.setmaxage(3600l);
urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource();
source.registercorsconfiguration("/**",corsconfiguration);
return source;
}
}到此这篇关于springsecurity中的跨域问题处理方案的文章就介绍到这了,更多相关springsecurity跨域内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论