背景
早期為了避免 CSRF(跨站請求偽造) 攻擊,瀏覽器引入了 “同源策略” 機制。如果兩個 URL 的協議,主機名(域名/IP),端口號一致,則視為這兩個 URL “同源”,屬於同一個 “域”,否則視為 “非同源”,即 “跨域”。瀏覽器會主動攔截跨域的 AJAX 請求,以規避安全風險。
“同源策略” 固然提升了請求的安全性,但有時我們需要跨域請求其他域名下的資源,例如在業務域名下請求 COS 的 API 接口,或者讀取 COS 存儲桶中文件的內容,進行一些邏輯處理。
瀏覽器支持一種跨域的訪問驗證的機制,即 CORS(Cross-Origin Resource Sharing 跨源資源共享)。該機制允許服務端通過返回特定的 HTTP 頭部來告知瀏覽器是否攔截跨域請求。
COS 支持用戶在存儲桶中配置 “跨域訪問 CORS” 規則,以此放行一些合法的跨域請求。
業務場景
下面我們以 博客網站開發 為例,帶您了解如何在 COS 配置 CORS 規則。
用戶正在開發一個博客網站,為此他將一批 markdown 文件上傳到 COS,對每個 markdown 文件設置了自定義頭部 x-cos-meta-keywords 表示該文章的關鍵詞,並將文件權限設置為公有讀私有寫。
網站的前端 JS 腳本通過瀏覽器向 COS 發起 AJAX 請求,讀取響應的內容以及頭部信息,將內容轉換為 HTML 文本,解析 x-cos-meta-keywords 中包含的關鍵詞,分別掛載到頁面對應的 DOM 節點下,其請求過程如下圖所示:
用戶網站的地址是 http://example.com,Markdown 文件的地址是 https://bucketname-1250000000.cos.ap-guangzhou.myqcloud.com/test.md
很顯然,這是在 http://example.com 下對 https://bucketname-1250000000.cos.ap-guangzhou.myqcloud.com 發起請求,涉及跨域。
不出意外地,瀏覽器攔截了該請求,打開瀏覽器的 Console 欄可以看到如下報錯:
Access to XMLHttpRequest at 'https://bucketname-1250000000.cos.ap-guangzhou.myqcloud.com/test.md'
from origin 'http://example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET https://bucketname-1250000000.cos.ap-guangzhou.myqcloud.com/test.md net::ERR_FAILED
CORS 跨域訪問機制
為了解決該問題,我們需要理解瀏覽器的 CORS 跨域訪問機制。瀏覽器將 CORS 請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request),只要同時滿足以下兩大條件,就屬於 “簡單請求”,否則就屬於 “非簡單請求”。
請求方法是以下三種方法之一:
- HEAD
- GET
- POST
HTTP 的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限於三個值 application/x-www-form-urlencoded、multipart/form-data、text/plain
對於簡單請求,瀏覽器會在請求時攜帶 Origin 頭部,聲明該請求來自哪個 “域”,服務端通過 Access-Control-Allow-Origin 和Access-Control-Allow-Methods 響應頭來告知瀏覽器允許的 “域” 和 “HTTP 動詞”。
對於非簡單請求,瀏覽器會在實際請求前發起一次 “預檢請求(preflight request)”,用於詢問服務端是否允許跨域,如果不允許,則不發起實際請求。
預檢請求使用 OPTIONS 動詞,並攜帶 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 請求頭部,來聲明 “當前的域”、“實際請求使用的 HTTP 動詞” 和 “實際請求將攜帶的頭部” 等信息。
服務端通過 Access-Control-Allow-Origin、 Access-Control-Allow-Methods 、Access-Control-Allow-Headers、Access-Control-Allow-Credentials 響應頭來告知瀏覽器 “允許的域”、“允許的所有 HTTP 動詞”、“允許攜帶的所有 HTTP 頭部” 以及 “是否允許攜帶鑒權信息(Cookie,Authorization 頭部等)”。
同時,為了避免頻繁發起跨域檢測,服務端會返回 Access-Control-Max-Age 來聲明本次跨域檢測的有效期,瀏覽器會緩存檢測結果,並在有效期內使用瀏覽器緩存。
服務端還可以通過返回 Access-Control-Expose-Headers 頭部來告知瀏覽器,哪些特定響應頭允許暴露給 AJAX 請求。通常只有 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma 允許暴露,其他頭部需要服務端特定聲明。
瀏覽器會根據預檢請求響應的 CORS 相關頭部進行判斷,只有實際請求的 Origin,Methods,Headers 等均符合要求,才會發起實際請求。
在 COS 配置 CORS 跨域規則
了解了 CORS 跨域訪問機制后,我們看看用戶需要配置的哪些 CORS 響應頭部。
- 需配置Access-Control-Allow-Origin,必須包含 http://example.com,表示允許 http://example.com 對 COS 的跨域訪問。
- 需配置Access-Control-Allow-Methods,必須包含GET方法。
- 需配置Access-Control-Expose-Headers,必須包含自定義頭部 x-cos-meta-keywords,表示允許暴露該響應頭部。
於是用戶進入 COS 控制台,點擊進入存儲桶,在左側的 “安全設置” 中選擇 “跨域訪問 CORS 設置”,點擊添加規則,按如下規則填寫:
- 來源 Origin:填入 http://example.com(也可以填寫 *,代表允許所有域)
- 操作 Methods:勾選 GET
- Allow-Headers:設置為 *,代表請求允許攜帶任何頭部
- Expose-Headers:添加 x-cos-meta-keywords
實際填寫如下圖所示,點擊確定。
再次嘗試剛剛的跨域請求,可以看到,跨域請求成功,並返回了文件內容以及自定義頭部信息。
更進一步,用戶還希望在網站上添加 “保存文章”,“刪除文章” 等功能,為了降低開發成本,我們推薦其使用 cos-js-sdk-v5。為避免后續其他請求的跨域問題,我們推薦進行如下設置:
- 來源 Origin:填入 http://example.com(填寫您的域名,須包含協議)
- 操作 Methods:勾選 PUT、GET、POST、DELETE、HEAD
- Allow-Headers:設置為 *,代表請求允許攜帶任何頭部
- Expose-Headers:添加 ETag,x-cos-request-id 頭部,確保 SDK 可以讀取到需要的頭部
- 超時 Max-Age:設置為 600,讓瀏覽器緩存跨域檢測結果,過期時間為 600 秒
CDN 上配置 CORS 規則
如果開通了 CDN 服務,並且設置 COS 為 CDN 的源站,由於 CDN 會緩存 COS 的響應結果,包括跨域響應頭部。通過 CDN 域名訪問 COS 上的文件時,如果希望響應的跨域頭部為最新配置,可以在 CDN 控制台的 “Response Header 配置” 中設置 CORS 相關跨域頭部,如下圖所示:
可以看到,跨域請求 CDN 加速域名下的資源成功,響應的跨域頭部和 CDN 控制台配置的一致。
結語
全文通過博客網站開發,瀏覽器主動攔截跨域的 AJAX 請求的場景,詳細介紹了 CORS 跨域訪問機制,以及如何在 COS 和 CDN 上配置 CORS 跨域規則。
此外,對象存儲 COS 的 CORS 跨域機制基於存儲桶可以配置多條跨域訪問規則,允許 Web 應用服務器進行跨域訪問控制,使得跨域數據傳輸得以安全進行,簡單易用,無需額外的第三方工具操作。滿足客戶 Web 應用需要跨域訪問存儲桶資源的需求,幫助您構建內容豐富的 Web 應用。
COS 一直在不斷地豐富產品特性,幫助用戶更加輕松的使用對象存儲 COS,讓用戶聚焦於數據內容,為業務賦能,釋放數據價值。