笔记:同源政策、跨域资源共享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-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在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))