浅谈CORS(跨域资源分享),并给出Spring Security处理Preflight的方法

来源:互联网 发布:量化炒股软件 编辑:程序博客网 时间:2024/06/10 04:20

我之前就写过有关AngularJS+Spring Boot在有关跨域方面的博客,但那主要是针对解决问题,而没有去分析跨域。后来我在看了多篇博客之后,在这里整理总结下我对CORS的认识。

造成跨域的主要原因是由于请求方的1、域名 2、端口与被请求方不一致。而CORS可以提供跨域的资源分享(比如一个网站需要访问另一个网站的资源,这个资源可以是一张图片,或者是一个API接口),它需要前后端共同去实现。

支持CORS的浏览器有

1、Chrome 3+

2、Firefox 3.5+

3、Opera 12+

4、Safari 4+

5、IE 8+

CORS的请求方式分两种:简单请求和非简单请求。

简单请求满足下列两点:

1、HTTP Method为下列三种:HEAD、GET、POST

2、HTTP Headers中主要包括Accept、Accept-Language、Content-Language、Last-Event-ID以及Content-Type(Content-Type只能取1application/x-www-form-urlencoded、2multipart/form-data、3text/plain这三种)。

其他情况都属于非简单请求。

像目前比较常用的JSON-P来进行GET方法的跨域,用的就是简单请求。但是如果用JSON,那肯定就是非简单请求了。

下面我们来看简单请求:

首先用Javascript发送请求时与平时一样,但是Request的内容会发生改变,下图是我引用https://www.html5rocks.com/en/tutorials/cors/中的图片。

红色框框的部分是浏览器自动给我们加上的,Origin中的值主要有协议、域名、端口这几种。注意,CORS请求肯定有Origin这个属性,但是有这个属性并不一定是CORS请求。

当服务器接受到这个请求之后,它会返回给我们一个请求,这个Response请求中会包含下面几个属性:

Access-Control-Allow-Origin属性为服务器允许跨域访问的域名、

Access-Control-Allow-Credentials为是否允许写入Cookies。这个属性大家需要注意,它是一个可选属性,但是如果你要用到Session必须要打开,,所有的Cookie都由你请求的站点来控制,你是无法通过JavaScript来管理的。

Access-Control-Expose-Headers主要携带特殊Response中Header的信息。

下面来看看非简单请求:

当我们要发送PUT、DELETE请求,或者使用Json,那我我们会发送非简单请求,下面看张图。


这张图中我们可以看出,非简单请求在发送真正的请求前会发送一次Preflight Request,接收一个Preflight Response。(这也是Preflight恶心的地方)。

下面我们来看一个Preflight Request:

这个Preflight Request不会携带Cookie和你要传递的参数,它携带

1、Origin 告诉服务器我的域名是多少

2、Access-Control-Request-Method 我真实请求的方法是什么

3、Access-Control-Request-Headers 真实请求携带的Header中的信息

然后服务器端给我们返回一个Preflight Response

主要到这个Response返回给我们的Methods包含了GET,POST,DELETE,PUT这个4个方法,而不是我们请求的GET,那是因为PreflightResponse可以缓存一段时间,在这段时间内,非简单请求就以这个Response为参考,符合这些条件就不必再发Preflight,而是直接开始真实的请求。

然后浏览器开始发送真实的请求:

上图为真实的请求。大家注意到这个请求携带了Cookie,这是因为我在发出请求时,就设置了.withCredentials=true(如果不知道如何设置,大家可以百度)

然后服务器会给我返回相应的Response,这个Response与简单请求的差不多。


以上就是CORS请求,浏览器与服务器交互的过程,这上面的坑主要是Preflight,如果我们的后台用了安全管理框架(比如Spring Security),并且没有对Preflight这个请求做出相应的处理,那么这个请求会导致权限管控失败(比如无法登录)。因为Preflight不携带Cookie,即不携带JSESSIONID,因此Spring Security拦截器会认为你没有登录。

在用Spring Security作为安全框架的情况下,处理这种问题也是非常简单的,下面给上代码

 @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()        .antMatchers("/login").permitAll()        .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()//就是这一行啦        .antMatchers("/admin/**").hasRole("ADMIN")        .antMatchers("/teacher/**").hasRole("TEACHER")        .antMatchers("/student/**").hasRole("STUDENT")        .anyRequest().authenticated()        .and().formLogin().permitAll()        .and().formLogin().successHandler(authenticationSuccessionHandler)        .and().formLogin().failureUrl("http://localhost:63342/yjsy-ui/build/login/login.html")        .and().csrf().disable();    }
requestMatchers(CorsUtils::isPreFlightRequest).permitAll()的作用是将PreflightRequest不做拦截。

1 0