笔记:同源政策、跨域资源共享CORS、aiohttp的CORS实现

2020-03-12:

读后笔记,原文跳转:

1、浏览器同源政策及其规避方法,阮一峰,link:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

2、跨域资源共享 CORS 详解,阮一峰,link:https://www.ruanyifeng.com/blog/2016/04/cors.html

3、aiohttp_cors 库,为aiohttp异步HTTP服务器实现了 跨源资源共享(CORS)支持,link:https://github.com/aio-libs/aiohttp-cors#usage

 

cookie:Cookie 包含隐私(比如存款总额)。Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户。(后面出一个blog详细学习cookie)

 


 

一、什么是浏览器同源政策:

1、同源:协议(http)、域名(www.xxx.com)、端口(8080)都相同,称其为同源。

2、目的:保护用户信息安全,a网站设置的cookie只能用于同源网站。"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了

3、同源带来的限制:

  a、Cookie、LocalStorage 和 IndexDB 无法读取;

  b、DOM 无法获得;

  c、AJAX 请求不能发送。(重点来了,这个导致了CORS的出现)

 

4、如何规避上述限制:

(1)、一级域名相同二级域名不同时如何共享cookie? : 浏览器设置 document.domain='xxx.com'; 以共享 Cookie。另外,服务器端设置:Set-Cookie: doman='.xxx.com'; path=/

(2)iframe窗口window.open方法打开的窗口,它们与父窗口无法通信,原因是两个网页不同源无法拿到对方的DOM,如何解决? :

  a、一级域名相同,二级域名不同,方法同上,设置document.domain

  b、两个网站完全不同源:(iframe跨域问题)

    i、片段识别符(fragment identifier):URL的#号后面的部分。父窗口可以把信息写入子窗口的片段识别符,子窗口通过监听hashchange事件得到通知。反之,子窗口也可以改变父窗口的片段识别符。

    ii、浏览器窗口的window.name 属性:无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。优点是,window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

    iii、html5新api,跨文档通信API,新增 window.postMessage 方法,父窗口和子窗口都可以通过message事件,监听对方的消息。

(3)localStorage 的跨域解决:利用window.postMessage 方法,把localStorage将通过消息发送进行通讯。

(4)ajax跨域请求:

  a、服务器代理:浏览器请求同源服务器,再由后者请求外部服务;

  b、JSONP:网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。缺点:只能发GET请求

  c、websocket : WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。其中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

  d、CORS:下文详述。

 

二、什么是跨源资源分享(CORS):

1、条件:需要浏览器、服务器同时支持,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

2、分类:简单请求和非简单请求。区分点,满足的就是简单请求:

 

3、简单请求:

  对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)

  怎么知道请求是否通过?-- 无论如何,浏览器都会返回200,所以状态码不可靠。需要通过回应的头信息没有包含Access-Control-Allow-Origin 判断是否成功,这部分浏览器会自动检查和发起错误。

  -- Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:

  (1)Access-Control-Allow-Origin;该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

  (2)Access-Control-Allow-Credentials; (可选,服务器端)表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。另一方面,必须在AJAX请求中打开withCredentials属性。如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

  (3)Access-Control-Expose-Headers; (可选)CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

4、非简单请求:

  (略)

5、CORS与JSONP的比较:

  JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

 

三、aiohttp-cors 的应用:

  要使用aiohttp_cors,您需要配置应用并在要公开的资源和路由上启用CORS:

# 1、基本用法:

from aiohttp import web
import aiohttp_cors

app = web.Application()
cors = aiohttp_cors.setup(app)  # 返回`aiohttp_cors.CorsConfig`实例

# 要为特定路线使用CORS,您需要添加路由到CORS配置对象并指定其CORS选项。
resource = cors.add(app.router.add_resource("/hello"))
route = cors.add(
    resource.add_route("GET", handler), {
        "http://client.example.org": aiohttp_cors.ResourceOptions(
            allow_credentials=True,   # 是否允许证书
            expose_headers=("X-Custom-Server-Header",),  # 该资源将暴露X-Custom-Server-Header 给客户端。
            allow_headers=("X-Requested-With", "Content-Type"),  # 客户端被允许把这些header传给服务端
            max_age=3600,  # 客户端可以将预检请求缓存3600秒
        )
    })

 

# 2、资源可以针对不同源可以有不同的规则:

cors.add(route, {
        "*":
            aiohttp_cors.ResourceOptions(allow_credentials=False),
        "http://client.example.org":
            aiohttp_cors.ResourceOptions(allow_credentials=True),
    })


#  “*”(除xxx之外所有其他源):允许向其提供内容,但不允许向其提供证书;
#  with allowed credentials passing only to http://client.example.org.

 

# 3、指定默认的启用CORS的资源选项,然后再补充该资源只能被请求的源和请求的方式:

cors = aiohttp_cors.setup(app, defaults={
        # Allow all to read all CORS-enabled resources from
        # http://client.example.org.
        "http://client.example.org": aiohttp_cors.ResourceOptions(),
    })

hello_resource = cors.add(app.router.add_resource("/hello"))
cors.add(hello_resource.add_route("POST", handler_post))
cors.add(hello_resource.add_route("PUT", handler_put))

# 也能这么写:
hello_resource = cors.add(app.router.add_resource("/hello"), {
        "http://client.example.org": aiohttp_cors.ResourceOptions(),
    })
cors.add(hello_resource.add_route("POST", handler_post))
cors.add(hello_resource.add_route("PUT", handler_put))

# 还能这么写:
hello_resource = cors.add(app.router.add_resource("/hello"), {
        "http://client.example.org":
            aiohttp_cors.ResourceOptions(allow_methods=["POST", "PUT"]),
    })

 

# 4、例子:允许所有源使用包含所有功能的CORS

cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

# Add all resources to `CorsConfig`.
resource = cors.add(app.router.add_resource("/hello"))
cors.add(resource.add_route("GET", handler_get))
cors.add(resource.add_route("PUT", handler_put))
cors.add(resource.add_route("POST", handler_put))
cors.add(resource.add_route("DELETE", handler_delete))

 

posted @ 2020-03-14 00:12  Marvin_Tang  阅读(1460)  评论(1编辑  收藏  举报