最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go gorilla securecookie庫(kù)的安裝使用詳解

 更新時(shí)間:2022年08月12日 14:46:19   作者:darjun  
這篇文章主要介紹了Go gorilla securecookie庫(kù)的安裝使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

簡(jiǎn)介

cookie 是用于在 Web 客戶端(一般是瀏覽器)和服務(wù)器之間傳輸少量數(shù)據(jù)的一種機(jī)制。由服務(wù)器生成,發(fā)送到客戶端保存,客戶端后續(xù)的每次請(qǐng)求都會(huì)將 cookie 帶上。cookie 現(xiàn)在已經(jīng)被多多少少地濫用了。很多公司使用 cookie 來(lái)收集用戶信息、投放廣告等。

cookie 有兩大缺點(diǎn):

  • 每次請(qǐng)求都需要傳輸,故不能用來(lái)存放大量數(shù)據(jù);
  • 安全性較低,通過(guò)瀏覽器工具,很容易看到由網(wǎng)站服務(wù)器設(shè)置的 cookie。

gorilla/securecookie提供了一種安全的 cookie,通過(guò)在服務(wù)端給 cookie 加密,讓其內(nèi)容不可讀,也不可偽造。當(dāng)然,敏感信息還是強(qiáng)烈建議不要放在 cookie 中。

快速使用

本文代碼使用 Go Modules。

創(chuàng)建目錄并初始化:

$ mkdir gorilla/securecookie && cd gorilla/securecookie
$ go mod init github.com/darjun/go-daily-lib/gorilla/securecookie

安裝gorilla/securecookie庫(kù):

$ go get github.com/gorilla/securecookie
package main
import (
  "fmt"
  "github.com/gorilla/mux"
  "github.com/gorilla/securecookie"
  "log"
  "net/http"
)
type User struct {
  Name string
  Age int
}
var (
  hashKey = securecookie.GenerateRandomKey(16)
  blockKey = securecookie.GenerateRandomKey(16)
  s = securecookie.New(hashKey, blockKey)
)
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
  u := &User {
    Name: "dj",
    Age: 18,
  }
  if encoded, err := s.Encode("user", u); err == nil {
    cookie := &http.Cookie{
      Name: "user",
      Value: encoded,
      Path: "/",
      Secure: true,
      HttpOnly: true,
    }
    http.SetCookie(w, cookie)
  }
  fmt.Fprintln(w, "Hello World")
}
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
  if cookie, err := r.Cookie("user"); err == nil {
    u := &User{}
    if err = s.Decode("user", cookie.Value, u); err == nil {
      fmt.Fprintf(w, "name:%s age:%d", u.Name, u.Age)
    }
  }
}
func main() {
  r := mux.NewRouter()
  r.HandleFunc("/set_cookie", SetCookieHandler)
  r.HandleFunc("/read_cookie", ReadCookieHandler)
  http.Handle("/", r)
  log.Fatal(http.ListenAndServe(":8080", nil))
}

首先需要?jiǎng)?chuàng)建一個(gè)SecureCookie對(duì)象:

var s = securecookie.New(hashKey, blockKey)

其中hashKey是必填的,它用來(lái)驗(yàn)證 cookie 是否是偽造的,底層使用 HMAC(Hash-based message authentication code)算法。推薦hashKey使用 32/64 字節(jié)的 Key。

blockKey是可選的,它用來(lái)加密 cookie,如不需要加密,可以傳nil。如果設(shè)置了,它的長(zhǎng)度必須與對(duì)應(yīng)的加密算法的塊大小(block size)一致。例如對(duì)于 AES 系列算法,AES-128/AES-192/AES-256 對(duì)應(yīng)的塊大小分別為 16/24/32 字節(jié)。

為了方便也可以使用GenerateRandomKey()函數(shù)生成一個(gè)安全性足夠強(qiáng)的隨機(jī) key。每次調(diào)用該函數(shù)都會(huì)返回不同的 key。上面代碼就是通過(guò)這種方式創(chuàng)建 key 的。

調(diào)用s.Encode("user", u)將對(duì)象u編碼成字符串,內(nèi)部實(shí)際上使用了標(biāo)準(zhǔn)庫(kù)encoding/gob。所以gob支持的類型都可以編碼。

調(diào)用s.Decode("user", cookie.Value, u)將 cookie 值解碼到對(duì)應(yīng)的u對(duì)象中。

運(yùn)行:

$ go run main.go

首先使用瀏覽器訪問(wèn)localhost:8080/set_cookie,這時(shí)可以在 Chrome 開(kāi)發(fā)者工具的 Application 頁(yè)簽中看到 cookie 內(nèi)容:

訪問(wèn)localhost:8080/read_cookie,頁(yè)面顯示name: dj age: 18。

使用 JSON

securecookie默認(rèn)使用encoding/gob編碼 cookie 值,我們也可以改用encoding/json。securecookie將編解碼器封裝成一個(gè)Serializer接口:

type Serializer interface {
  Serialize(src interface{}) ([]byte, error)
  Deserialize(src []byte, dst interface{}) error
}

securecookie提供了GobEncoderJSONEncoder的實(shí)現(xiàn):

func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
  buf := new(bytes.Buffer)
  enc := gob.NewEncoder(buf)
  if err := enc.Encode(src); err != nil {
    return nil, cookieError{cause: err, typ: usageError}
  }
  return buf.Bytes(), nil
}
func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
  dec := gob.NewDecoder(bytes.NewBuffer(src))
  if err := dec.Decode(dst); err != nil {
    return cookieError{cause: err, typ: decodeError}
  }
  return nil
}
func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
  buf := new(bytes.Buffer)
  enc := json.NewEncoder(buf)
  if err := enc.Encode(src); err != nil {
    return nil, cookieError{cause: err, typ: usageError}
  }
  return buf.Bytes(), nil
}
func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
  dec := json.NewDecoder(bytes.NewReader(src))
  if err := dec.Decode(dst); err != nil {
    return cookieError{cause: err, typ: decodeError}
  }
  return nil
}

我們可以調(diào)用securecookie.SetSerializer(JSONEncoder{})設(shè)置使用 JSON 編碼:

var (
  hashKey = securecookie.GenerateRandomKey(16)
  blockKey = securecookie.GenerateRandomKey(16)
  s = securecookie.New(hashKey, blockKey)
)
func init() {
  s.SetSerializer(securecookie.JSONEncoder{})
}

自定義編解碼

我們可以定義一個(gè)類型實(shí)現(xiàn)Serializer接口,那么該類型的對(duì)象可以用作securecookie的編解碼器。我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 XML 編解碼器:

package main
type XMLEncoder struct{}
func (x XMLEncoder) Serialize(src interface{}) ([]byte, error) {
  buf := &bytes.Buffer{}
  encoder := xml.NewEncoder(buf)
  if err := encoder.Encode(buf); err != nil {
    return nil, err
  }
  return buf.Bytes(), nil
}
func (x XMLEncoder) Deserialize(src []byte, dst interface{}) error {
  dec := xml.NewDecoder(bytes.NewBuffer(src))
  if err := dec.Decode(dst); err != nil {
    return err
  }
  return nil
}
func init() {
  s.SetSerializer(XMLEncoder{})
}

由于securecookie.cookieError未導(dǎo)出,XMLEncoderGobEncoder/JSONEncoder返回的錯(cuò)誤有些不一致,不過(guò)不影響使用。

Hash/Block 函數(shù)

securecookie默認(rèn)使用sha256.New作為 Hash 函數(shù)(用于 HMAC 算法),使用aes.NewCipher作為 Block 函數(shù)(用于加解密):

// securecookie.go
func New(hashKey, blockKey []byte) *SecureCookie {
  s := &SecureCookie{
    hashKey:   hashKey,
    blockKey:  blockKey,
    // 這里設(shè)置 Hash 函數(shù)
    hashFunc:  sha256.New,
    maxAge:    86400 * 30,
    maxLength: 4096,
    sz:        GobEncoder{},
  }
  if hashKey == nil {
    s.err = errHashKeyNotSet
  }
  if blockKey != nil {
    // 這里設(shè)置 Block 函數(shù)
    s.BlockFunc(aes.NewCipher)
  }
  return s
}

可以通過(guò)securecookie.HashFunc()修改 Hash 函數(shù),傳入一個(gè)func () hash.Hash類型:

func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
  s.hashFunc = f
  return s
}

通過(guò)securecookie.BlockFunc()修改 Block 函數(shù),傳入一個(gè)f func([]byte) (cipher.Block, error)

func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
  if s.blockKey == nil {
    s.err = errBlockKeyNotSet
  } else if block, err := f(s.blockKey); err == nil {
    s.block = block
  } else {
    s.err = cookieError{cause: err, typ: usageError}
  }
  return s
}

更換這兩個(gè)函數(shù)更多的是處于安全性的考慮,例如選用更安全的sha512算法:

s.HashFunc(sha512.New512_256)

更換 Key

為了防止 cookie 泄露造成安全風(fēng)險(xiǎn),有個(gè)常用的安全策略:定期更換 Key。更換 Key,讓之前獲得的 cookie 失效。對(duì)應(yīng)securecookie庫(kù),就是更換SecureCookie對(duì)象:

var (
  prevCookie    unsafe.Pointer
  currentCookie unsafe.Pointer
)
func init() {
  prevCookie = unsafe.Pointer(securecookie.New(
    securecookie.GenerateRandomKey(64),
    securecookie.GenerateRandomKey(32),
  ))
  currentCookie = unsafe.Pointer(securecookie.New(
    securecookie.GenerateRandomKey(64),
    securecookie.GenerateRandomKey(32),
  ))
}

程序啟動(dòng)時(shí),我們先生成兩個(gè)SecureCookie對(duì)象,然后每隔一段時(shí)間就生成一個(gè)新的對(duì)象替換舊的。

由于每個(gè)請(qǐng)求都是在一個(gè)獨(dú)立的 goroutine 中處理的(讀),更換 key 也是在一個(gè)單獨(dú)的 goroutine(寫(xiě))。為了并發(fā)安全,我們必須增加同步措施。但是這種情況下使用鎖又太重了,畢竟這里更新的頻率很低。

我這里將securecookie.SecureCookie對(duì)象存儲(chǔ)為unsafe.Pointer類型,然后就可以使用atomic原子操作來(lái)同步讀取和更新了:

func rotateKey() {
  newcookie := securecookie.New(
    securecookie.GenerateRandomKey(64),
    securecookie.GenerateRandomKey(32),
  )
  atomic.StorePointer(&prevCookie, currentCookie)
  atomic.StorePointer(&currentCookie, unsafe.Pointer(newcookie))
}

rotateKey()需要在一個(gè)新的 goroutine 中定期調(diào)用,我們?cè)?code>main函數(shù)中啟動(dòng)這個(gè) goroutine

func main() {
  ctx, cancel := context.WithCancel(context.Background())
  defer cancel()
  go RotateKey(ctx)
}
func RotateKey(ctx context.Context) {
  ticker := time.NewTicker(30 * time.Second)
  defer ticker.Stop()
  for {
    select {
    case <-ctx.Done():
      break
    case <-ticker.C:
    }
    rotateKey()
  }
}

這里為了方便測(cè)試,我設(shè)置每隔 30s 就輪換一次。同時(shí)為了防止 goroutine 泄漏,我們傳入了一個(gè)可取消的Context。還需要注意time.NewTicker()創(chuàng)建的*time.Ticker對(duì)象不使用時(shí)需要手動(dòng)調(diào)用Stop()關(guān)閉,否則會(huì)造成資源泄漏。

使用兩個(gè)SecureCookie對(duì)象之后,我們編解碼可以調(diào)用EncodeMulti/DecodeMulti這組方法,它們可以接受多個(gè)SecureCookie對(duì)象:

func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
  u := &User{
    Name: "dj",
    Age:  18,
  }
  if encoded, err := securecookie.EncodeMulti(
    "user", u,
    // 看這里 ??
    (*securecookie.SecureCookie)(atomic.LoadPointer(&currentCookie)),
  ); err == nil {
    cookie := &http.Cookie{
      Name:     "user",
      Value:    encoded,
      Path:     "/",
      Secure:   true,
      HttpOnly: true,
    }
    http.SetCookie(w, cookie)
  }
  fmt.Fprintln(w, "Hello World")
}

使用unsafe.Pointer保存SecureCookie對(duì)象后,使用時(shí)需要類型轉(zhuǎn)換。并且由于并發(fā)問(wèn)題,需要使用atomic.LoadPointer()訪問(wèn)。

解碼時(shí)調(diào)用DecodeMulti依次傳入currentCookieprevCookie,讓prevCookie不會(huì)立刻失效:

func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
  if cookie, err := r.Cookie("user"); err == nil {
    u := &User{}
    if err = securecookie.DecodeMulti(
      "user", cookie.Value, u,
      // 看這里 ??
      (*securecookie.SecureCookie)(atomic.LoadPointer(&currentCookie)),
      (*securecookie.SecureCookie)(atomic.LoadPointer(&prevCookie)),
    ); err == nil {
      fmt.Fprintf(w, "name:%s age:%d", u.Name, u.Age)
    } else {
      fmt.Fprintf(w, "read cookie error:%v", err)
    }
  }
}

運(yùn)行程序:

$ go run main.go

先請(qǐng)求localhost:8080/set_cookie,然后請(qǐng)求localhost:8080/read_cookie讀取 cookie。等待 1 分鐘后,再次請(qǐng)求,發(fā)現(xiàn)之前的 cookie 失效了:

read cookie error:securecookie: the value is not valid (and 1 other error)

總結(jié)

securecookie為 cookie 添加了一層保護(hù)罩,讓 cookie 不能輕易地被讀取和偽造。還是需要強(qiáng)調(diào)一下:

敏感數(shù)據(jù)不要放在 cookie 中!敏感數(shù)據(jù)不要放在 cookie 中!敏感數(shù)據(jù)不要放在 cookie 中!敏感數(shù)據(jù)不要放在 cookie 中!

重要的事情說(shuō) 4 遍。在使用 cookie 存放數(shù)據(jù)時(shí)需要仔細(xì)權(quán)衡。

大家如果發(fā)現(xiàn)好玩、好用的 Go 語(yǔ)言庫(kù),歡迎到 Go 每日一庫(kù) GitHub 上提交 issue??

參考

gorilla/securecookie GitHub:github.com/gorilla/securecookie

Go 每日一庫(kù) GitHub:https://github.com/darjun/go-daily-lib

以上就是Go gorilla securecookie庫(kù)的安裝使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Go gorilla securecookie庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go 語(yǔ)言數(shù)組和切片的區(qū)別詳解

    Go 語(yǔ)言數(shù)組和切片的區(qū)別詳解

    本文主要介紹了Go 語(yǔ)言數(shù)組和切片的區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go經(jīng)典面試題匯總(填空+判斷)

    Go經(jīng)典面試題匯總(填空+判斷)

    這篇文章主要介紹了Go經(jīng)典面試題匯總(填空+判斷),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下
    2023-01-01
  • 一文帶你搞懂Golang結(jié)構(gòu)體內(nèi)存布局

    一文帶你搞懂Golang結(jié)構(gòu)體內(nèi)存布局

    結(jié)構(gòu)體在Go語(yǔ)言中是一個(gè)很重要的部分,在項(xiàng)目中會(huì)經(jīng)常用到。這篇文章主要帶大家看一下結(jié)構(gòu)體在內(nèi)存中是怎么分布的?通過(guò)對(duì)內(nèi)存布局的了解,可以幫助我們寫(xiě)出更優(yōu)質(zhì)的代碼。感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助
    2022-10-10
  • Go語(yǔ)言讀取文本文件的三種方式總結(jié)

    Go語(yǔ)言讀取文本文件的三種方式總結(jié)

    工作中時(shí)不時(shí)需要讀取文本,文本文件是最常見(jiàn)的文件類型。本文將利用Go語(yǔ)言從逐行、逐個(gè)單詞和逐個(gè)字符三個(gè)方法讀取文件,感興趣的可以了解一下
    2023-01-01
  • go語(yǔ)言中切片的長(zhǎng)度和容量的區(qū)別

    go語(yǔ)言中切片的長(zhǎng)度和容量的區(qū)別

    這篇文章主要介紹了go語(yǔ)言中切片的長(zhǎng)度和容量的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 一文讓你理解go語(yǔ)言的Context

    一文讓你理解go語(yǔ)言的Context

    在Go語(yǔ)言中,Context(上下文)是一個(gè)類型,用于在程序中傳遞請(qǐng)求范圍的值、截止時(shí)間、取消信號(hào)和其他與請(qǐng)求相關(guān)的上下文信息,它在多個(gè)goroutine之間傳遞這些值,使得并發(fā)編程更加可靠和簡(jiǎn)單,本文詳細(xì)介紹go語(yǔ)言的Context,需要的朋友可以參考下
    2023-05-05
  • golang微服務(wù)框架kratos實(shí)現(xiàn)Socket.IO服務(wù)的方法

    golang微服務(wù)框架kratos實(shí)現(xiàn)Socket.IO服務(wù)的方法

    本文主要介紹了golang微服務(wù)框架kratos實(shí)現(xiàn)Socket.IO服務(wù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Go模板template用法詳解

    Go模板template用法詳解

    這篇文章主要介紹了Go標(biāo)準(zhǔn)庫(kù)template模板用法詳解;包括GO模板注釋,作用域,語(yǔ)法,函數(shù)等知識(shí),需要的朋友可以參考下
    2022-04-04
  • gRPC中攔截器的使用詳解

    gRPC中攔截器的使用詳解

    這篇文章主要介紹了gRPC中攔截器的使用詳解,本次主要介紹在gRPC中使用攔截器,包括一元攔截器和流式攔截器,在攔截器中添加JWT認(rèn)證,客戶端登錄之后會(huì)獲得token,請(qǐng)求特定的API時(shí)候需要帶上token才能訪問(wèn),需要的朋友可以參考下
    2023-10-10
  • 使用Go實(shí)現(xiàn)在命令行輸出好看的表格

    使用Go實(shí)現(xiàn)在命令行輸出好看的表格

    這篇文章主要介紹了使用Go實(shí)現(xiàn)在命令行輸出好看的表格方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04

最新評(píng)論

樟树市| 南漳县| 宜城市| 静乐县| 祁东县| 淳化县| 贡山| 客服| 滦南县| 屏东县| 长宁区| 长治市| 来宾市| 龙口市| 二连浩特市| 璧山县| 临洮县| 梓潼县| 东安县| 张北县| 九龙坡区| 囊谦县| 丹寨县| 宝丰县| 扶余县| 镇远县| 馆陶县| 大姚县| 清丰县| 南城县| 太仆寺旗| 华蓥市| 泸定县| 九龙城区| 盱眙县| 稻城县| 余姚市| 如皋市| 镇赉县| 芦山县| 历史|