HTTP跨域请求

什么是跨域HTTP请求

HTML中a/form/img/css/script/iframe/ajax都可以指向资源
发起请求的资源所在域不同于该请求所指向资源所在的域的HTTP请求就叫做跨域HTTP请求

何为不同域

协议+域名+端口号都相同才是同域

跨域请求带来的问题

如果对跨域请求不做限制,会有安全隐患
例1:
一个恶意网站的页面通过iframe嵌入了支付宝的登录页面(两者不同域),如果没有任何限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码
例2:
跨域请求别人的AJAX登录接口,也可以获取到用户名密码

如何限制跨域请求

  1. 不允许跨域请求
    NO
  2. 服务端
    可以根据相关的HEADER判断来源,限制访问
    但是HEADER可以伪造
  3. 浏览器
    发起跨域请求时,进行限制
    同源策略

    同源策略

    是浏览器的一种约定和安全功能,对跨域请求进行控制
    1:限制来自不同源的document或脚本,对当前document读取或设置某些属性
    2:禁止ajax直接发起跨域HTTP请求(其实可以发送请求,结果被浏览器拦截,不展示)
    3:允许img/css/script(拥有src的标签都有跨域的能力。加载下来就属于当前域了)
    4:允许跨域的连接、跳转、表单提交(带来了跨站请求伪造CSRF问题)

    同源策略的作用

    解决了例1里面的问题:
    恶意网站的JS无法读取iframe里面的内容
    因为iframe加载之后属于单独的一个window当然也是一个单独的document
    解决了例2里面的问题:
    发起的AJAX请求结果如何被浏览器屏蔽了,虽然实际请求已经发送到服务端,但是不知道结果(所以服务端也要有相应的策略)

    如何越过限制

    在此不讨论JS如何读取iframe的document元素的问题.只涉及如何越过浏览器限制,发起AJAX跨域请求并显示结果
    AJAX跨域请求实现方式:
    1:通过代理
    自己的服务器实现接口,后台调用其他域的接口
    2:JSONP
    服务端调用本地js,并将结果带回
    3:CORS
    扩展HTTP协议,根据服务端返回的有关HEADER来决定是否展示结果

JSONP

JSON Padding
JSON是一种数据交换格式,JSONP是一种ajax跨域实现方案
1:script不受同源策略限制能加载到
2:demo.js里面能调用本地的js方法
这就是JSONP(可能不准确…)
jquery等已经对jsonp进行了很好的实现与封装,可以google一下,不做过多介绍
缺点是:
1:服务端需要指定JS的方法名
2:只支持GET

CORS

CORS
跨源资源共享 Cross-Origin Resource Sharing
是一个规范,可以简单理解为对HTTP协议进行了扩展,增加了一些http header,让服务器能声明那些来源可以通过浏览器访问该服务器上的资源
Request headers
• Origin
• Access-Control-Request-Method
• Access-Control-Request-Headers
Response headers
• Access-Control-Allow-Origin
• Access-Control-Allow-Credentials
• Access-Control-Expose-Headers
• Access-Control-Max-Age
• Access-Control-Allow-Methods
• Access-Control-Allow-Headers

头部介绍

Origin
参数origin是一个URI,告诉服务器端,请求来自哪里.
浏览器发起http请求时一般都会设置这个头,不是CORS特有
Access-Control-Request-Method
在发出预检请求时,告诉服务器在实际请求时使用的请求方式
Access-Control-Request-Headers
在发出预检请求时,告诉服务器在实际请求时携带的自定义头信息.如有多个,可以用逗号分开
Access-Control-Allow-Origin
指定一个允许向该服务器提交请求的URI.对于一个不带有credentials的请求,可以指定为*,表示允许来自所有域的请求.
Access-Control-Expose-Headers
指明允许脚本访问的响应头
Access-Control-Max-Age
缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据
Access-Control-Allow-Credentials
是否允许请求带有Cookie信息
Access-Control-Allow-Methods
在响应预检请求的时候使用.指明资源可以被请求的方式有哪些(一个或者多个).
Access-Control-Allow-Headers
在响应预检请求的时候使用.用来指明在实际的请求中,可以使用哪些自定义HTTP请求头

CORS如何跨域
简单的HTTP请求

只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。 不会使用自定义请求头(类似于 X-Modified 这种) 直接发起请求

复杂的HTTP请求

浏览器必须先以 OPTIONS 请求方式发送一个预请求(preflight request),从而获知服务器端对跨源请求所支持 HTTP 方法。在确认服务器允许该跨源请求的情况下,以实际的 HTTP 请求方法发送那个真正的请求。服务器端也可以通知客户端,是不是需要随同请求一起发送信用信息(包括 Cookies 和 HTTP 认证相关数据)

CORS例子
简单请求的CORS示例

GET /resources/public-data/ HTTP/1.1
Host: bar.other
Referer: http://foo.example/examples/access-control/a.html
Origin: http://foo.example
省略其他header

HTTP/1.1 200 OK
Access-Control-Allow-Origin:
省略其他header
服务器返回了响应头 Access-Control-Allow-Origin:
,这表明服务器接受来自任何站点的跨站请求。
如果服务器端仅允许来自 http://foo.example 的跨站请求,它可以返回: Access-Control-Allow-Origin: http://foo.example

复杂请求的CORS示例

为该请求添加了一个自定义请求头(X-PINGOTHER: pingpong 所以,该请求是一个预请求形式的跨站请求。
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000

POST /resources/post-here/ HTTP/1.1
X-PINGOTHER: pingpong
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example

CORS的实现

CORS的实现
1:现在大多数浏览器都支持
2:服务端程序可以自己设置cors的响应头,java里面可以通过servlet的filter
3:SpringMVC对cors的支持