go語言csrf庫使用實(shí)現(xiàn)原理示例解析
引言
今天給大家推薦的是web應(yīng)用安全防護(hù)方面的一個(gè)包:csrf。該包為Go web應(yīng)用中常見的跨站請(qǐng)求偽造(CSRF)攻擊提供預(yù)防功能。
csrf小檔案
「csrf小檔案」 | |||
---|---|---|---|
star | 837 | used by | - |
contributors | 25 | 作者 | Gorilla |
功能簡介 | 為Go web應(yīng)用程序和服務(wù)提供跨站點(diǎn)請(qǐng)求偽造(csrf)預(yù)防功能。可作為gin、echo等主流框架的中間件使用。 | ||
項(xiàng)目地址 | github.com/gorilla/csr… | ||
相關(guān)知識(shí) | 跨站請(qǐng)求偽造(CSRF)、contex.Contex、異或操作 |
一、CSRF及其實(shí)現(xiàn)原理
CSRF是CROSS Site Request Forgy的縮寫,即跨站請(qǐng)求偽造。我們看下他的攻擊原理。如下圖:
當(dāng)用戶訪問一個(gè)網(wǎng)站的時(shí)候,第一次登錄完成后,網(wǎng)站會(huì)將驗(yàn)證的相關(guān)信息保存在瀏覽器的cookie中。在對(duì)該網(wǎng)站的后續(xù)訪問中,瀏覽器會(huì)自動(dòng)攜帶該站點(diǎn)下的cookie信息,以便服務(wù)器校驗(yàn)認(rèn)證信息。
因此,當(dāng)服務(wù)器經(jīng)過用戶認(rèn)證之后,服務(wù)器對(duì)后續(xù)的請(qǐng)求就只認(rèn)cookie中的認(rèn)證信息,不再區(qū)分請(qǐng)求的來源了。那么,攻擊者就可以模擬一個(gè)正常的請(qǐng)求來做一些影響正常用戶利益的事情(比如對(duì)于銀行來說可以把用戶的錢轉(zhuǎn)賬到攻擊者賬戶中?;颢@取用戶的敏感、重要的信息等)
相關(guān)知識(shí):因?yàn)榈卿浶畔⑹腔趕ession-cookie的。瀏覽器在訪問網(wǎng)站時(shí)會(huì)自動(dòng)發(fā)送該網(wǎng)站的cookie信息,網(wǎng)站只要能識(shí)別cookie中的信息,就會(huì)認(rèn)為是認(rèn)證已通過,而不會(huì)區(qū)分該請(qǐng)求的來源的。所以給攻擊者創(chuàng)造了攻擊的機(jī)會(huì)。
CSRF攻擊示例
假設(shè)有一個(gè)銀行網(wǎng)站A,下面的是一個(gè)轉(zhuǎn)給賬戶5000元的請(qǐng)求,使用Get方法
GET https://abank.com/transfer.do?account=RandPerson&amount=$5000 HTTP/1.1
然后,攻擊者修改了該請(qǐng)求中的參數(shù),將收款賬戶更改成了自己的,如下:
GET https://abank.com/transfer.do?account=SomeAttacker&amount=$5000 HTTP/1.1
然后,攻擊者將該請(qǐng)求地址放入到一個(gè)標(biāo)簽中:
<a rel="external nofollow" >Click for more information</a>
最后,攻擊者會(huì)以各種方式(放到自己的網(wǎng)站中、email、社交通訊工具等)引誘用戶點(diǎn)擊該鏈接。只要是用戶點(diǎn)擊了該鏈接,并且在之前已經(jīng)登錄了該網(wǎng)站,那么瀏覽器就會(huì)將帶認(rèn)證信息的cookie自動(dòng)發(fā)送給該網(wǎng)站,網(wǎng)站認(rèn)為這是一個(gè)正常的請(qǐng)求,由此,將給黑客轉(zhuǎn)賬5000元。造成合法用戶的損失。
當(dāng)然,如果是post表單形式,那么攻擊者會(huì)將偽造的鏈接放到form表達(dá)中,并用js的方法讓表單自動(dòng)發(fā)送:
<body onload="document.forms[0].submit()> <form id=”csrf” action="https://abank.com/transfer.do" method="POST"> <input type="hidden" name="account" value="SomeAttacker"/> <input type="hidden" name="amount" value="$5000"/> </form> </body> <script> document.getElementById('csrf').submit(); </script>
二、如何預(yù)防
常見的有3種方法:
- 一種是在網(wǎng)站中增加對(duì)請(qǐng)求來源的驗(yàn)證,比如在請(qǐng)求頭中增加REFFER信息。
- 一種是在瀏覽器中啟用SameSite策略。該策略是告訴瀏覽器,只有請(qǐng)求來源是同網(wǎng)站的才能發(fā)送cookie,跨站的請(qǐng)求不要發(fā)送cookie。但這種也有漏洞,就是依賴于瀏覽器是否支持這種策略。
- 一種是使用Token信息。由網(wǎng)站自己決定token的生成策略以及對(duì)token的驗(yàn)證。
其中使用Token信息這種是三種方法中最安全的一種。接下來我們就看看今天要推薦的CSRF包是如何利用token進(jìn)行預(yù)防的。
三、CSRF包的使用及實(shí)現(xiàn)原理
csrf包的安裝
go get github.com/gorilla/csrf
基本使用
該包主要包括三個(gè)功能:
- 通過csrf.Protect函數(shù)生成一個(gè)csrf中間件或請(qǐng)求處理器,用于后續(xù)的生成及校驗(yàn)token的流程。
- 通過csrf.Token函數(shù),可以在響應(yīng)中輸出當(dāng)前生成的token值。
- 通過csrf.TemplateField函數(shù),可以在html模版中輸出一個(gè)hidden的input,用于在form表單中提交token。
該包的使用很簡單。首先通過csrf.Protect函數(shù)生成一個(gè)中間件或請(qǐng)求處理器,然后在啟動(dòng)web server時(shí)對(duì)真實(shí)的請(qǐng)求處理器進(jìn)行包裝。
我們來看下該包和主流web框架結(jié)合使用的實(shí)例。
使用net/http包啟動(dòng)的服務(wù)
package main import ( "fmt" "github.com/gorilla/csrf" "net/http" ) func main() { muxServer := http.NewServeMux() muxServer.HandleFunc("/", IndexHandler) CSRF := csrf.Protect([]byte("32-byte-long-auth-key")) http.ListenAndServe(":8000", CSRF(muxServer)) } func IndexHandler(w http.ResponseWriter, r *http.Request) { // 獲取token值 token := csrf.Token(r) // 將token寫入到header中 w.Header().Set("X-CSRF-Token", token) fmt.Fprintln(w, "hello world.Go") }
echo框架下使用csrf包
package main import ( "github.com/gorilla/csrf" "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.POST("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) // 使用自定義的CSRF中間件 e.Use(CSRFMiddle()) e.Logger.Fatal(e.Start(":8080")) } // 自定義CSRF中間件 func CSRFMiddle() echo.MiddlewareFunc { csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key")) // 這里使用echo的WrapMiddleware函數(shù)將csrfMiddleware轉(zhuǎn)換成echo的中間件返回值 return echo.WrapMiddleware(csrfMiddleware) }
gin框架下使用csrf包
import ( "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/csrf" adapter "github.com/gwatts/gin-adapter" ) // 定義中間件 func CSRFMiddle() gin.HandlerFunc { csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key")) // 這里使用adpater包將csrfMiddleware轉(zhuǎn)換成gin的中間件返回值 return adapter.Wrap(csrfMiddleware) } func main() { r := gin.New() // 在路由中使用中間件 r.Use(CSRFMiddle()) // 定義路由 r.POST("/", IndexHandler) // 啟動(dòng)http服務(wù) r.Run(":8080") } func IndexHandler(ctx *gin.Context) { ctx.String(200, "hello world") }
beego框架下使用csrf包
package main import ( "github.com/beego/beego" "github.com/gorilla/csrf" ) func main() { beego.Router("/", &MainController{}) beego.RunWithMiddleWares(":8080", CSRFMiddle()) } type MainController struct { beego.Controller } func (this *MainController) Get() { this.Ctx.Output.Body([]byte("Hello World")) } func CSRFMiddle() beego.MiddleWare { csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key")) // 這里使用adpater包將csrfMiddleware轉(zhuǎn)換成gin的中間件返回值 return csrfMiddleware }
實(shí)際上,要通過token預(yù)防CSRF主要做以下3件事情:每次生成一個(gè)唯一的token、將token寫入到cookie同時(shí)下發(fā)給客戶端、校驗(yàn)token。接下來我們就來看看csrf包是如何實(shí)現(xiàn)如上步驟的。
實(shí)現(xiàn)原理
csrf結(jié)構(gòu)體
該包的實(shí)現(xiàn)是基于csrf這樣一個(gè)結(jié)構(gòu)體:
type csrf struct { h http.Handler sc *securecookie.SecureCookie st store opts options }
該結(jié)構(gòu)體同時(shí)實(shí)現(xiàn)了一個(gè)ServeHTTP方法:
func (cs *csrf) ServeHTTP(w http.ResponseWriter, r *http.Request)
在Go中,我們知道ServeHTTP是在內(nèi)建包net/http中定義的一個(gè)請(qǐng)求處理器的接口:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
凡是實(shí)現(xiàn)了該接口的結(jié)構(gòu)體就能作為請(qǐng)求的處理器。在go的所有web框架中,處理器本質(zhì)上也都是基于該接口實(shí)現(xiàn)的。
好了,現(xiàn)在我們來分析下csrf這個(gè)結(jié)構(gòu)體的成員:
- 「h」:是一個(gè)http.Handler,作為實(shí)際處理請(qǐng)求的處理器。該h的來源是經(jīng)Protect函數(shù)返回值包裝后的,即開始示例中CSRF(muxServer)中的muxServer。又因?yàn)閏srf也是一個(gè)請(qǐng)求處理器,請(qǐng)求就會(huì)先執(zhí)行csrf的ServeHTTP方法的邏輯,如果通過了,再執(zhí)行h的ServeHTTP邏輯。
- 「sc」:類型是*securecookie.SecureCookie,第三方包,該包的作用是對(duì)cookie的值進(jìn)行加密/解密。在調(diào)用csrf.Protect方法時(shí),傳遞的第一個(gè)32字節(jié)長的參數(shù)就是用于該包進(jìn)行對(duì)稱加密用的秘鑰。下一篇文章我們會(huì)詳細(xì)介紹該包是如何實(shí)現(xiàn)對(duì)cookie內(nèi)容進(jìn)行/加解密的。
- 「st」:類型是store,是csrf包中定義的一個(gè)接口類型。該屬性的作用是將token存儲(chǔ)在什么地方。默認(rèn)是使用cookieStore類型。即將token存儲(chǔ)在cookie中。
- 「opts」:Options屬性,用于設(shè)置csrf的選項(xiàng)的。比如token存儲(chǔ)在cookie中的名字,token在表單中的名字等。
這里大家可能有這樣一個(gè)疑問:csrf攻擊就是基于cookie來進(jìn)行攻擊的,為什么還要把token存儲(chǔ)在cookie中呢?在一次請(qǐng)求中,會(huì)有兩個(gè)地方存儲(chǔ)token:一個(gè)是cookie中,一個(gè)是請(qǐng)求體中(query中,header中,或form中),當(dāng)服務(wù)端收到請(qǐng)求時(shí),會(huì)同時(shí)取出這兩個(gè)地方的token,進(jìn)而進(jìn)行比較。所以如果攻擊者偽造了一個(gè)請(qǐng)求,服務(wù)器能接收到cookie中的token,但不能接收到請(qǐng)求體中的token,所以偽造的攻擊還是無效的。
csrf包的工作流程
在開始的“使用net/http包啟動(dòng)的服務(wù)”示例中,我們先調(diào)用了Protect方法,然后又用返回值對(duì)muxServer進(jìn)行了包裝。大家是不是有點(diǎn)云里霧里,為什么要這么調(diào)用呢?接下來咱們就來分析下Protect這個(gè)函數(shù)以及csrf包的工作流程。
在使用csrf的時(shí)候,首先要調(diào)用的就是Protect函數(shù)。Protect的定義如下:
func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler
該函數(shù)接收一個(gè)秘鑰和一個(gè)選項(xiàng)切片參數(shù)。返回值是一個(gè)函數(shù)類型:func(http.Handler) http.Handler。實(shí)際的執(zhí)行邏輯是在返回的函數(shù)中。如下:
CSRF := csrf.Protect([]byte("32-byte-long-auth-key")) http.ListenAndServe(":8000", CSRF(muxServer)) // Protect源碼 func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { cs := parseOptions(h, opts...) // Set the defaults if no options have been specified if cs.opts.ErrorHandler == nil { cs.opts.ErrorHandler = http.HandlerFunc(unauthorizedHandler) } if cs.opts.MaxAge < 0 { // Default of 12 hours cs.opts.MaxAge = defaultAge } if cs.opts.FieldName == "" { cs.opts.FieldName = fieldName } if cs.opts.CookieName == "" { cs.opts.CookieName = cookieName } if cs.opts.RequestHeader == "" { cs.opts.RequestHeader = headerName } // Create an authenticated securecookie instance. if cs.sc == nil { cs.sc = securecookie.New(authKey, nil) // Use JSON serialization (faster than one-off gob encoding) cs.sc.SetSerializer(securecookie.JSONEncoder{}) // Set the MaxAge of the underlying securecookie. cs.sc.MaxAge(cs.opts.MaxAge) } if cs.st == nil { // Default to the cookieStore cs.st = &cookieStore{ name: cs.opts.CookieName, maxAge: cs.opts.MaxAge, secure: cs.opts.Secure, httpOnly: cs.opts.HttpOnly, sameSite: cs.opts.SameSite, path: cs.opts.Path, domain: cs.opts.Domain, sc: cs.sc, } } return cs } }
Protect的實(shí)現(xiàn)源碼起始很簡單,就是在一個(gè)閉包中初始化了一個(gè)csrf結(jié)構(gòu)體。示例中CSRF就是返回來的func(http.Handler) http.Handler
函數(shù)。再調(diào)用CSRF(muxServer),執(zhí)行初始化csrf結(jié)構(gòu)體的實(shí)例,同時(shí)將muxServer包裝到csrf結(jié)構(gòu)體的h屬性上,最后將該csrf結(jié)構(gòu)體對(duì)象返回。因?yàn)閏srf結(jié)構(gòu)體也實(shí)現(xiàn)了ServeHTTP接口,所以csrf自然也就是可以處理請(qǐng)求的http.Handler類型了。
當(dāng)一個(gè)請(qǐng)求來了之后,先執(zhí)行csrf結(jié)構(gòu)體中的ServeHTTP方法,然后再執(zhí)行實(shí)際的http.Handler。以最開始的請(qǐng)求為例,csrf包的工作流程如下:
大致了解了csrf的工作流程后,我們?cè)賮矸治龈鱾€(gè)環(huán)節(jié)的實(shí)現(xiàn)。
「生成唯一的token」
在該包中生成隨機(jī)、唯一的token是通過隨機(jī)數(shù)來生成的。主要生成邏輯如下:
func generateRandomBytes(n int) ([]byte, error) { b := make([]byte, n) _, err := rand.Read(b) // err == nil only if len(b) == n if err != nil { return nil, err } return b, nil }
crypto/rand包中的rand.Read函數(shù)可以隨機(jī)生成指定字節(jié)個(gè)數(shù)的隨機(jī)數(shù)。但這里出的隨機(jī)數(shù)是字節(jié)值,如果序列化成字符串則會(huì)是亂碼。那如何將字節(jié)序列序列化成可見的字符編碼呢?那就是對(duì)字節(jié)進(jìn)行編碼。這里使用的是標(biāo)準(zhǔn)庫中的encoding/json包。該包能夠?qū)Ω鞣N類型進(jìn)行可視化編碼。如果對(duì)字節(jié)序列進(jìn)行編碼,本質(zhì)上是使用了base64的標(biāo)準(zhǔn)編碼。如下:
realToken := generateRandomBytes(32) //編碼后,encodeToken是base64編碼的字符串 encodeToken := json.Encode(realToken)
「token的存儲(chǔ)位置」
生成token之后,token會(huì)存儲(chǔ)在兩個(gè)位置:
- 一個(gè)是隨響應(yīng)將token寫入cookie中。在cookie中的token將用于下次請(qǐng)求的基準(zhǔn)token和請(qǐng)求中攜帶的token進(jìn)行比較。該實(shí)現(xiàn)是通過csrf中的cookieStore來存儲(chǔ)到cookie中的(store類型)。在cookie中name默認(rèn)是
_gorilla_csrf
。同時(shí),通過cookieStore類型存儲(chǔ)到cookie的值是經(jīng)過加密的,加密使用的是securecookie.SecureCookie包 - 一個(gè)是存儲(chǔ)在請(qǐng)求的上下文中。存在這里的token是原始token經(jīng)過轉(zhuǎn)碼的,會(huì)隨著響應(yīng)下發(fā)給客戶端,以便下次請(qǐng)求時(shí)隨請(qǐng)求體一起發(fā)送。該實(shí)現(xiàn)是通過context.ValueContext存儲(chǔ)在請(qǐng)求的上下文中。
生成token后為什么要存在cookie中呢?CSRF的攻擊原理不就是基于瀏覽器自動(dòng)發(fā)送cookie造成的嗎?攻擊者偽造的請(qǐng)求還是會(huì)直接從cookie中獲取token,附帶在請(qǐng)求中不就行了嗎?答案是否定的。在請(qǐng)求中保存的token,是經(jīng)過轉(zhuǎn)碼后的,跟cookie中的token不一樣。在收到請(qǐng)求時(shí),再對(duì)token進(jìn)行解碼,然后再和cookie中的token進(jìn)行比較??聪孪旅娴膶?shí)現(xiàn):
func mask(realToken []byte, r *http.Request) string { otp, err := generateRandomBytes(tokenLength) if err != nil { return "" } // XOR the OTP with the real token to generate a masked token. Append the // OTP to the front of the masked token to allow unmasking in the subsequent // request. return base64.StdEncoding.EncodeToString(append(otp, xorToken(otp, realToken)...)) }
這里我們看到,先生成一個(gè)和token一樣長度的隨機(jī)值otp,然后讓實(shí)際的realToken和opt通過xorToken進(jìn)行異或操作,將異或操作的結(jié)果放到隨機(jī)值的末尾,然后再進(jìn)行base64編碼產(chǎn)生的。
假設(shè)一個(gè)token是32位的字節(jié),那么最終的maskToken由64位組成。前32位是otp的隨機(jī)值,后32位是異或之后的token。兩個(gè)組合起來就是最終的maskToken。如下圖:
這里利用了異或操作的原理來進(jìn)行轉(zhuǎn)碼和解碼。我們假設(shè)A ^ B = C
。那么會(huì)有 A = C ^ B
所以,要想還原異或前的真實(shí)token值,則從maskToken中取出前32個(gè)字節(jié)和后32字節(jié),再進(jìn)行異或操作就能得到真實(shí)的token了。然后就可以和cookie中存儲(chǔ)的真實(shí)的token進(jìn)行比較了。同時(shí)因?yàn)榻?jīng)過異或轉(zhuǎn)碼的token,攻擊者想要進(jìn)行偽造就很難了。
「輸出token」
在上述我們已經(jīng)知道經(jīng)過異或操作對(duì)原始token進(jìn)行了轉(zhuǎn)碼,我們叫做maskToken。該token要下發(fā)給客戶端(HEADER、form或其他位置)。那么,客戶端用什么字段來接收呢?
默認(rèn)情況下,maskToken是存儲(chǔ)在以下位置的:
- 若在HEADER頭中,則保存在名為 X-CSRF-Token 的字段中。
- 若在form表單,則保存在名為 gorilla.csrf.Token 的input中。
當(dāng)然,我們?cè)诔跏蓟痗srf的實(shí)例時(shí),可以指定保存的位置。例如,我們指定HEADER頭中的字段名為 X-CSRF-Token-Request中,則可以使用如下代碼:
csrf.Protect([]byte("32-byte-long-auth-key"), RequestHeader("X-CSRF-Token-Request"))
csrf中可以指定的選項(xiàng)如下:
- RequestHeader選項(xiàng)函數(shù):指定在HEADER中存儲(chǔ)token的字段名稱。
- FieldName選項(xiàng)函數(shù):指定form表中存儲(chǔ)token的input的name
- MaxAge選項(xiàng)函數(shù):指定cookie中值的有效期
- Domain選項(xiàng)函數(shù):指定cookie的存儲(chǔ)域名
- Path選項(xiàng)函數(shù):指定cookie的存儲(chǔ)路徑
- HttpOnly選項(xiàng)函數(shù):指定cookie的值只能在服務(wù)端設(shè)置,禁止在客戶端使用javascript修改
- SameSite選項(xiàng)函數(shù):指定cookie的SameSite屬性
- ErrorHandler選項(xiàng)函數(shù):指定當(dāng)token校驗(yàn)不通過或生成token失敗時(shí)的錯(cuò)誤響應(yīng)的handler
「更新token」
在調(diào)用csrf.ServeHTTP函數(shù)中,每次都會(huì)生成一個(gè)新的token,存儲(chǔ)在對(duì)應(yīng)的位置上,同時(shí)下發(fā)給客戶端,以便該請(qǐng)求的后續(xù)請(qǐng)求攜帶token值給服務(wù)端進(jìn)行驗(yàn)證。所以,該請(qǐng)求之前的token也就失效了。
為什么GET、HEAD、OPTIONS、TRACE的請(qǐng)求方法不需要token驗(yàn)證
在csrf包中,我們還看到有這么一段判斷邏輯:
// Idempotent (safe) methods as defined by RFC7231 section 4.2.2. safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"} if !contains(safeMethods, r.Method) { //這里進(jìn)行token的校驗(yàn) }
為什么GET、HEAD、OPTIONS、TRACE方法的請(qǐng)求不需求token驗(yàn)證呢?因?yàn)楦鶕?jù)RFC7231文檔的規(guī)定,這些方法的請(qǐng)求本質(zhì)上是一種 冪等 的訪問方法,這說明開發(fā)web的時(shí)候g這些請(qǐng)求不應(yīng)該用于修改數(shù)據(jù)庫狀態(tài),而只作為一個(gè)請(qǐng)求訪問或者鏈接跳轉(zhuǎn)。通俗地講,發(fā)送一個(gè)GET請(qǐng)求不應(yīng)該引起任何數(shù)據(jù)狀態(tài)的改變。用于修改狀態(tài)更加合適的是post方法,特別是對(duì)用戶信息狀態(tài)改變的情況。
所以,如果嚴(yán)格按照RFC的規(guī)定來開發(fā)的話,這些請(qǐng)求不應(yīng)該修改數(shù)據(jù),而只是獲取數(shù)據(jù)。獲取數(shù)據(jù)對(duì)于攻擊者來說也沒實(shí)際價(jià)值。
總結(jié)
CSRF攻擊是基于將驗(yàn)證信息存儲(chǔ)于cookie中,同時(shí)瀏覽器在發(fā)送請(qǐng)求時(shí)會(huì)自動(dòng)攜帶cookie的原理進(jìn)行的。所以,其預(yù)防原理也就是驗(yàn)證請(qǐng)求來源的真實(shí)性。csrf包就是利用了token校驗(yàn)的原理,讓前后連續(xù)的請(qǐng)求簽發(fā)token、下次請(qǐng)求驗(yàn)證token的方式進(jìn)行預(yù)防的。
以上就是go語言csrf庫使用實(shí)現(xiàn)原理示例解析的詳細(xì)內(nèi)容,更多關(guān)于go語言csrf庫使用原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang實(shí)現(xiàn)加權(quán)輪詢負(fù)載均衡算法
加權(quán)輪詢負(fù)載均衡算法是一種常見的負(fù)載均衡策略,本文主要介紹了Golang實(shí)現(xiàn)加權(quán)輪詢負(fù)載均衡算法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08解決Golang map range遍歷結(jié)果不穩(wěn)定問題
這篇文章主要介紹了解決Golang map range遍歷結(jié)果不穩(wěn)定問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12Go項(xiàng)目中添加生成時(shí)間與版本信息的方法
本文主要介紹了Go項(xiàng)目中添加生成時(shí)間與版本信息的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Go 代碼規(guī)范錯(cuò)誤處理示例經(jīng)驗(yàn)總結(jié)
這篇文章主要為大家介紹了Go 代碼規(guī)范錯(cuò)誤處理示例實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Go語言實(shí)現(xiàn)的一個(gè)簡單Web服務(wù)器
這篇文章主要介紹了Go語言實(shí)現(xiàn)的一個(gè)簡單Web服務(wù)器,本文先是給出一個(gè)使用http包建立的Web服務(wù)器源碼,并對(duì)比了其它編程語言,需要的朋友可以參考下2014-10-10Go語言實(shí)現(xiàn)socket實(shí)例
這篇文章主要介紹了Go語言實(shí)現(xiàn)socket的方法,實(shí)例分析了socket客戶端與服務(wù)器端的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02