使用go語(yǔ)言實(shí)現(xiàn)cors中間件
一、概述
CORS(Cross-Origin Resource Sharing)是一種瀏覽器安全機(jī)制,用于控制在Web應(yīng)用程序中不同源(Origin)之間的資源共享。一個(gè)源是由協(xié)議(例如http或https)、主機(jī)(例如 www.example.com)、以及端口(例如80或443)組成的組合。CORS允許服務(wù)器定義哪些源可以訪問(wèn)其資源,以及哪些HTTP方法和頭部可以在跨源請(qǐng)求中使用。
要點(diǎn):
- 同源策略(Same-Origin Policy):Web瀏覽器實(shí)施了同源策略,它限制了一個(gè)頁(yè)面中加載的資源只能來(lái)自相同的源,以防止而已站點(diǎn)獲取用戶的敏感信息。這意味著默認(rèn)情況下,跨域請(qǐng)求是被瀏覽器禁止的。
- 跨源HTTP請(qǐng)求:當(dāng)一個(gè)Web頁(yè)面需要從不同源的服務(wù)器獲取數(shù)據(jù)時(shí),例如通過(guò)AJAX請(qǐng)求或嵌入其他站點(diǎn)的資源(如字體、圖片或腳本),就會(huì)設(shè)計(jì)到跨源HTTP請(qǐng)求。
- CORS解決跨域問(wèn)題: CORS是一種機(jī)制,允許服務(wù)器在響應(yīng)中添加HTTP標(biāo)頭來(lái)指示瀏覽器允許跨域請(qǐng)求。這些標(biāo)頭包括”Access-Control-Allow-Origin“、”Access-Control-Allow-Methods“、”Access-Control-Allow-Headers“等
- 簡(jiǎn)單請(qǐng)求和預(yù)檢請(qǐng)求:瀏覽器將跨域HTTP請(qǐng)求分為兩種類型:簡(jiǎn)單請(qǐng)求(Simple Request)和預(yù)檢請(qǐng)求(Preflight Request)。簡(jiǎn)單請(qǐng)求就是常見(jiàn)的GET、POST請(qǐng)求,而預(yù)檢請(qǐng)求時(shí)一種用于檢查服務(wù)器是否支持某些請(qǐng)求的OPTION請(qǐng)求。
- 服務(wù)器配置:CORS的服務(wù)器配置通常在服務(wù)器端完成。服務(wù)器需要響應(yīng)OPTIONS請(qǐng)求并在響應(yīng)頭中包含CORS標(biāo)頭來(lái)指定允許的來(lái)源、HTTP方法和頭部。服務(wù)器還可以選擇性地要求攜帶憑據(jù)(credentials)
- 瀏覽器行為:瀏覽器在發(fā)送跨域請(qǐng)求時(shí)會(huì)自動(dòng)附加Origin頭,然后根據(jù)服務(wù)器的響應(yīng)判斷是否允許訪問(wèn)資源。如果CORS設(shè)置不正確,瀏覽器會(huì)阻止頁(yè)面訪問(wèn)響應(yīng)的數(shù)據(jù)。
二、簡(jiǎn)單請(qǐng)求和預(yù)檢請(qǐng)求
簡(jiǎn)單請(qǐng)求
1、判定條件
需要同時(shí)滿足以下條件,瀏覽器會(huì)認(rèn)為它是一個(gè)簡(jiǎn)單請(qǐng)求
- 請(qǐng)求方法屬于其中一種:GET、POST、HEAD
- 請(qǐng)求包僅包含安全的字段,常見(jiàn)字段:Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
- 請(qǐng)求頭如果包含Content-Type,僅限如下值:text/plain、multipart/form-data、application/x-www-form-urlencoded
2、簡(jiǎn)單請(qǐng)求交互
①當(dāng)瀏覽器判某個(gè)跨域請(qǐng)求時(shí)簡(jiǎn)單請(qǐng)求時(shí),會(huì)在請(qǐng)求頭中自動(dòng)添加Origin字段
GET /cors HTTP/1.1 Host: example.com Connection: keep-alive ... Referer: http://local.com/index.html Origin: http://local.com //Origin字段會(huì)告訴服務(wù)器,是哪個(gè)源地址在跨域請(qǐng)求
②服務(wù)器響應(yīng)頭中應(yīng)包含Access-Control-Allow-Origin ,當(dāng)服務(wù)器收到請(qǐng)求后,如果允許改請(qǐng)求跨域訪問(wèn),需要在響應(yīng)頭中添加Access-Control-Allow-Origin字段
HTTP/1.1 200 OK Date: Tue, 21 Jun 2023 08:03:35 GMT ... Access-Control-Allow-Origin: http://local.com ... 消息體中的數(shù)據(jù)
當(dāng)瀏覽器看到服務(wù)器允許自己訪問(wèn)后,于是,它就把響應(yīng)順利的交給js
預(yù)檢請(qǐng)求
1、對(duì)于預(yù)檢請(qǐng)求,請(qǐng)求步驟如下:
- 瀏覽器發(fā)送預(yù)檢請(qǐng)求,詢問(wèn)服務(wù)器是否允許
- 服務(wù)器響應(yīng)是否允許
- 瀏覽器發(fā)送真實(shí)請(qǐng)求
- 服務(wù)器完成真實(shí)的響應(yīng)
2、步驟請(qǐng)求演示
要發(fā)送的請(qǐng)求包:
POST /cors HTTP/1.1
Host: example.com
Connection: keep-alive ...
Referer: http://local.com/index.html
Origin: http://local.com
{"name": "admin", "age": 20}
①瀏覽器發(fā)送預(yù)檢請(qǐng)求
OPTIONS /cors HTTP/1.1 Host: example.com ... Origin: http://local.com Access-Control-Request-Method: POST Access-Control-Request-Headers: a, b, content-type
ps:這并非想要發(fā)出的真實(shí)請(qǐng)求,請(qǐng)求中不包含響應(yīng)頭和也沒(méi)有消息體。
預(yù)檢請(qǐng)求特征:
請(qǐng)求方法為OPTIONS 沒(méi)有請(qǐng)求體
- 請(qǐng)求頭中包含 Origin:請(qǐng)求的源,和簡(jiǎn)單請(qǐng)求的含義一致
- Access-Control-Request-Method:后續(xù)的真實(shí)請(qǐng)求將使用的請(qǐng)求方法
- Access-Control-Request-Headers:后續(xù)的真實(shí)請(qǐng)求會(huì)改動(dòng)的請(qǐng)求頭
②服務(wù)器允許
服務(wù)器收到預(yù)檢請(qǐng)求后,可以檢查預(yù)檢請(qǐng)求中包含的信息,如果允許這樣的請(qǐng)求,需要響應(yīng)下面的消息格式
HTTP/1.1 200 OK ... Access-Control-Allow-Origin: http://local.com Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: a, b, content-type Access-Control-Max-Age: 86400 ...
對(duì)于預(yù)檢請(qǐng)求,不需要響應(yīng)任何的消息體,只需要在響應(yīng)頭中添加:
Access-Control-Allow-Origin:和簡(jiǎn)單請(qǐng)求一樣,表示允許的源 Access-Control-Allow-Methods:表示允許的后續(xù)真實(shí)的請(qǐng)求方法 Access-Control-Allow-Headers:表示允許改動(dòng)的請(qǐng)求頭 Access-Control-Max-Age:告訴瀏覽器,多少秒內(nèi),對(duì)于同樣的請(qǐng)求源、方法、頭,都不需要再發(fā)送預(yù)檢請(qǐng)求了
③瀏覽器發(fā)送真實(shí)請(qǐng)求
預(yù)檢被服務(wù)器允許后,瀏覽器就會(huì)發(fā)送真實(shí)請(qǐng)求了,上面的代碼會(huì)發(fā)送下面的請(qǐng)求數(shù)據(jù)
POST /cors HTTP/1.1
Host: example.com
Connection: keep-alive ...
Referer: http://local.com/index.html
Origin: http://local.com
{"name": "admin", "age": 20}④服務(wù)器響應(yīng)真實(shí)請(qǐng)求
HTTP/1.1 200 OK Date: Tue, 21 Jun 2023 08:03:35 GMT ... Access-Control-Allow-Origin: http://local.com ... 添加用戶成功
三、使用go的gin框架實(shí)現(xiàn)cors配置
使用go的gin框架的話,說(shuō)白了就是添加一個(gè)中間件,Gin官方提供CORS中間件,可以很方便的使用 cors解決跨域問(wèn)題。
1、安裝
使用命令安裝該中間件
go get github.com/gin-contrib/cors
2、函數(shù)
cors中間件提供三個(gè)函數(shù),代表三種使用方式,分別是NEW、DefaultConfig、Default。
- NEW方式
可以接收 CORS 中間件的配置項(xiàng),可通過(guò)自定義配置項(xiàng),滿足任意需要跨域的場(chǎng)景。
示例:
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://foo.com"},
AllowMethods: []string{"PUT", "PATCH"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool {
return origin == "https://github.com"
},
MaxAge: 12 * time.Hour,
}))
//New 函數(shù)接收配置項(xiàng),返回一個(gè)用戶自定義的 CORS 中間件,綁定到路由中。三、使用go的gin框架實(shí)現(xiàn)cors配置
使用go的gin框架的話,說(shuō)白了就是添加一個(gè)中間件,Gin官方提供CORS中間件,可以很方便的使用 cors解決跨域問(wèn)題。
1、安裝
使用命令安裝該中間件
go get github.com/gin-contrib/cors
2、函數(shù)
cors中間件提供三個(gè)函數(shù),代表三種使用方式,分別是NEW、DefaultConfig、Default。
- NEW方式
可以接收 CORS 中間件的配置項(xiàng),可通過(guò)自定義配置項(xiàng),滿足任意需要跨域的場(chǎng)景。
示例:
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://foo.com"},
AllowMethods: []string{"PUT", "PATCH"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool {
return origin == "https://github.com"
},
MaxAge: 12 * time.Hour,
}))
//New 函數(shù)接收配置項(xiàng),返回一個(gè)用戶自定義的 CORS 中間件,綁定到路由中。配置項(xiàng)說(shuō)明:
- AllowAllOrigins bool 允許所有請(qǐng)求源。
- AllowOrigins []string 指定允許請(qǐng)求源的列表,如果列表中存在
*,則允許所有請(qǐng)求源,默認(rèn)值是[]。 - AllowOriginFunc func(origin string) bool 接收參數(shù) origin,函數(shù)體中的驗(yàn)證邏輯返回是否允許跨域請(qǐng)求。該配置項(xiàng)優(yōu)先級(jí)高于 AllowOrigins []string,如果設(shè)置該配置項(xiàng),
AllowOrigins []string配置項(xiàng)的設(shè)置被忽略。 AllowMethods []string允許的請(qǐng)求方式,默認(rèn)值是GET,POST,PUT,PATCH,DELETE,HEAD,和OPTIONS。AllowHeaders []string用在對(duì)預(yù)請(qǐng)求的響應(yīng)中,指示實(shí)際的請(qǐng)求中可以使用哪些 HTTP 請(qǐng)求頭。AllowCredentials bool表示請(qǐng)求附帶請(qǐng)求憑據(jù)時(shí)是否響應(yīng)請(qǐng)求,例如cookie、HTTP authentication或客戶端 SSL 證書(shū)。ExposeHeaders []string可以在響應(yīng)中顯示的請(qǐng)求頭。MaxAge time.Duration指示預(yù)請(qǐng)求的結(jié)果能被緩存多久。AllowWildcard bool添加請(qǐng)求源是否允許使用通配符,例如http://some-domain/*,https://api.或http://some.*.subdomain.com。AllowBrowserExtensions bool允許使用常用的瀏覽器的擴(kuò)展模式。AllowWebSockets bool允許使用 WebSocket 協(xié)議。AllowFiles bool允許使用file://協(xié)議。
- DefaultConfig方式
默認(rèn)設(shè)置一些通用配置項(xiàng),我們可以直接使用,也可以在此基礎(chǔ)上添加我們需要的其他配置項(xiàng)。
func Default() gin.HandlerFunc {
config := DefaultConfig()
config.AllowAllOrigins = true
return New(config)
}3、某段配置代碼分析
//設(shè)置cors中間件
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method //獲取請(qǐng)求包的http請(qǐng)求方法
origin := c.Request.Header.Get("Origin") //獲取請(qǐng)求包的head頭的Origin參數(shù)值
var headerkeys []string
for k := range c.Request.Header {
headerkeys = append(headerkeys, k)
}
headerStr := strings.Join(headerkeys, ", ") //將請(qǐng)求包中head頭部分的參數(shù)用","連接成字符串
//檢查請(qǐng)求頭不為空
if headerStr != "" {
//如果請(qǐng)求頭不為空,將請(qǐng)求頭的鍵名添加到一個(gè)字符串中,以便在設(shè)置響應(yīng)頭時(shí)包含這些鍵名。
headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers,%s", headerStr)
} else {
headerStr = "access-control-allow-origin, access-control-allow-headers"
}
//檢查請(qǐng)求頭中origin是否為空
if origin != "" {
//設(shè)置響應(yīng)頭中的"Access-Control-Allow-Origin"字段,允許任何域名訪問(wèn)資源(這是一個(gè)簡(jiǎn)單的CORS策略,實(shí)際應(yīng)用中可能需要更嚴(yán)格的控制)。
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域關(guān)鍵設(shè)置 讓瀏覽器可以解析
c.Header("Access-Control-Max-Age", "172800") // 緩存請(qǐng)求信息 單位為秒
c.Header("Access-Control-Allow-Credentials", "false") // 跨域請(qǐng)求是否需要帶cookie信息 默認(rèn)設(shè)置為true
c.Set("content-type", "application/json") // 設(shè)置返回格式是json
}
//放行所有OPTIONS方法
if method == "OPTIONS" {
c.JSON(http.StatusOK, "Options Request!")
}
}
}以上就是使用go語(yǔ)言實(shí)現(xiàn)cors中間件的詳細(xì)內(nèi)容,更多關(guān)于go實(shí)現(xiàn)cors中間件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang中Error的設(shè)計(jì)與實(shí)踐詳解
這篇文章主要為大家詳細(xì)介紹了Golang中Error的設(shè)計(jì)以及是具體如何處理錯(cuò)誤的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08
Go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之選擇排序示例詳解
這篇文章主要為大家介紹了Go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之選擇排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
關(guān)于go-zero服務(wù)自動(dòng)收集問(wèn)題分析
這篇文章主要介紹了關(guān)于go-zero服務(wù)自動(dòng)收集問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
Go中RPC遠(yuǎn)程過(guò)程調(diào)用的實(shí)現(xiàn)
本文主要介紹了Go中RPC遠(yuǎn)程過(guò)程調(diào)用的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Go語(yǔ)言學(xué)習(xí)之接口類型(interface)詳解
接口是用來(lái)定義行為的類型,定義的行為不由接口直接實(shí)現(xiàn),而由通過(guò)方法由定義的類型實(shí)現(xiàn),本文就來(lái)和大家詳細(xì)講講Go語(yǔ)言中接口的使用吧2023-03-03
詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆
堆是一種非常常用的數(shù)據(jù)結(jié)構(gòu),它能夠支持在O(1)的時(shí)間復(fù)雜度獲取到最大值(或最小值)。本文主要介紹了如何實(shí)現(xiàn)支持O(log(n))隨機(jī)刪除元素的堆,需要的可以參考一下2022-09-09
淺析Golang開(kāi)發(fā)中g(shù)oroutine的正確使用姿勢(shì)
很多初級(jí)的Gopher在學(xué)習(xí)了goroutine之后,在項(xiàng)目中其實(shí)使用率不高,所以這篇文章小編主要來(lái)帶大家深入了解一下goroutine的常見(jiàn)使用方法,希望對(duì)大家有所幫助2024-03-03
Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)解析JSON數(shù)據(jù)的多種方式
本文主要介紹了Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)解析JSON數(shù)據(jù)的多種方式,包括map[string]interface{}、interface{}、json.RawMessage及第三方庫(kù)gjson/mapstructure,具有一定的參考價(jià)值,感興趣的可以了解一下2025-05-05

