跨域请求详解
跨域请求总体分为两种类型:简单请求和复杂请求,即simple request和preflight request。
一、简单请求
simple request的请求需要,满足以下条件:
1.请求方法只能是GET,HEAD,POST
2.Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
3.第二个条件中的Content-Type只能是application/x-www-form-urlencoded、multipart/form-data、text/plain其中的一种
4.没有特殊js代码(参考https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests)
对于simple request请求,尽管这个请求是跨域的,它也会被浏览器直接放行。
但是浏览器拿到response后并不会把 response 直接暴露给用户程序,而是去检查这个 response 的 headers 中有没有 Access-Control-Allow-Origin
,以及这个 header 的 value 包含 request 发出的地址(也就是“域”)。
如果两个条件都满足, response 会被返回给发出请求的程序;如果没有这个 header 或者 value 不对, response 就会被拦截下来,因为在浏览器看来,这个 response 不属于你,因为服务器没有明确允许你这个“域”来请求它。
二、复杂请求
对于复杂请求,浏览器先发送一个pre-flight请求,通常是一个OPTIONS方法的请求,根据服务器响应的response的headers进行校验,若校验通过,则表明服务器允许访问,再发送真正的请求。
通常要校验的数据项有:Access-Control-Allow-Origin
, Access-Control-Allow-Methods、Access-Control-Allow-Headers以及其他的
Access-Control-*数据项。
下面是我们的一个跨域请求示例。本机63342端口的网页通过ajax访问8080端口的接口,存在跨域问题。
第一个是pre-flight请求,服务器收到请求并响应,response header中返回了Access-Control-Allow-Origin、Access-Control-Allow-Methods以及Access-Control-Allow-Headers等,
表示当前服务器允许http://localhost:63342域的GET请求(并且该请求包含name为authorization的header数据)访问。如果不符合该条件则真正的请求不会被发送。
因为我们的请求符合服务器所要求的的条件,所以真正的请求被发送,请求数据如下:
我么可以看到请求成功,一个完整的跨域请求就完成了。
spring4.2之后提供了@CrossOrigin注解来表明接口的跨域特性。
springMVC的请求流程是Request->Dispatchservlet->HandlerMappin(uri和handler的映射关系)->HandlerAdapter(适配器用于执行handler)->ModerAndView->ViewResolver->Response
CrossOrigin注解在HandlerAdapter阶段起作用,当检测到请求是preflight请求(满足三个条件,请求方法为OPTIONS、请求head包含Origin和Access-Control-Request-Method)时,
spring给本次请求适配的handler是PreFlightHandler实例,该handler专门处理preflight请求,判断是否拒绝访问或者允许访问时允许访问的域、请求方法、请求必须的header。
真正的请求发送之后就是则会经过CorsInterceptor拦截器,其前置方法preHandle也会对跨域请求进行校验,校验通过则设置一些响应数据头,然后交给下一级拦截器或者controller处理,校验不通过则响应浏览器禁止访问。