中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go?gorilla?securecookie庫怎么安裝使用

發布時間:2022-08-13 09:58:09 來源:億速云 閱讀:108 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Go gorilla securecookie庫怎么安裝使用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Go gorilla securecookie庫怎么安裝使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

簡介

cookie 是用于在 Web 客戶端(一般是瀏覽器)和服務器之間傳輸少量數據的一種機制。由服務器生成,發送到客戶端保存,客戶端后續的每次請求都會將 cookie 帶上。cookie 現在已經被多多少少地濫用了。很多公司使用 cookie 來收集用戶信息、投放廣告等。

cookie 有兩大缺點:

  • 每次請求都需要傳輸,故不能用來存放大量數據;

  • 安全性較低,通過瀏覽器工具,很容易看到由網站服務器設置的 cookie。

gorilla/securecookie提供了一種安全的 cookie,通過在服務端給 cookie 加密,讓其內容不可讀,也不可偽造。當然,敏感信息還是強烈建議不要放在 cookie 中。

快速使用

本文代碼使用 Go Modules。

創建目錄并初始化:

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

安裝gorilla/securecookie庫:

$ 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))
}

首先需要創建一個SecureCookie對象:

var s = securecookie.New(hashKey, blockKey)

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

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

為了方便也可以使用GenerateRandomKey()函數生成一個安全性足夠強的隨機 key。每次調用該函數都會返回不同的 key。上面代碼就是通過這種方式創建 key 的。

調用s.Encode("user", u)將對象u編碼成字符串,內部實際上使用了標準庫encoding/gob。所以gob支持的類型都可以編碼。

調用s.Decode("user", cookie.Value, u)將 cookie 值解碼到對應的u對象中。

運行:

$ go run main.go

首先使用瀏覽器訪問localhost:8080/set_cookie,這時可以在 Chrome 開發者工具的 Application 頁簽中看到 cookie 內容:

Go?gorilla?securecookie庫怎么安裝使用

訪問localhost:8080/read_cookie,頁面顯示name: dj age: 18

使用 JSON

securecookie默認使用encoding/gob編碼 cookie 值,我們也可以改用encoding/jsonsecurecookie將編解碼器封裝成一個Serializer接口:

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

securecookie提供了GobEncoderJSONEncoder的實現:

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
}

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

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

自定義編解碼

我們可以定義一個類型實現Serializer接口,那么該類型的對象可以用作securecookie的編解碼器。我們實現一個簡單的 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未導出,XMLEncoderGobEncoder/JSONEncoder返回的錯誤有些不一致,不過不影響使用。

Hash/Block 函數

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

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

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

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

通過securecookie.BlockFunc()修改 Block 函數,傳入一個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
}

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

s.HashFunc(sha512.New512_256)

更換 Key

為了防止 cookie 泄露造成安全風險,有個常用的安全策略:定期更換 Key。更換 Key,讓之前獲得的 cookie 失效。對應securecookie庫,就是更換SecureCookie對象:

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),
  ))
}

程序啟動時,我們先生成兩個SecureCookie對象,然后每隔一段時間就生成一個新的對象替換舊的。

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

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

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

rotateKey()需要在一個新的 goroutine 中定期調用,我們在main函數中啟動這個 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()
  }
}

這里為了方便測試,我設置每隔 30s 就輪換一次。同時為了防止 goroutine 泄漏,我們傳入了一個可取消的Context。還需要注意time.NewTicker()創建的*time.Ticker對象不使用時需要手動調用Stop()關閉,否則會造成資源泄漏。

使用兩個SecureCookie對象之后,我們編解碼可以調用EncodeMulti/DecodeMulti這組方法,它們可以接受多個SecureCookie對象:

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對象后,使用時需要類型轉換。并且由于并發問題,需要使用atomic.LoadPointer()訪問。

解碼時調用DecodeMulti依次傳入currentCookieprevCookie,讓prevCookie不會立刻失效:

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)
    }
  }
}

運行程序:

$ go run main.go

先請求localhost:8080/set_cookie,然后請求localhost:8080/read_cookie讀取 cookie。等待 1 分鐘后,再次請求,發現之前的 cookie 失效了:

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

讀到這里,這篇“Go gorilla securecookie庫怎么安裝使用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

安丘市| 彰化县| 肃宁县| 文登市| 耒阳市| 梓潼县| 景洪市| 正阳县| 尖扎县| 义乌市| 彭阳县| 定南县| 晋城| 新邵县| 共和县| 珲春市| 金平| 霍林郭勒市| 汨罗市| 凭祥市| 银川市| 东乡| 肇源县| 金华市| 汝城县| 湖北省| 孝昌县| 颍上县| 扬州市| 鸡泽县| 永嘉县| 卓资县| 文安县| 沙坪坝区| 商城县| 郑州市| 大港区| 酒泉市| 溆浦县| 慈溪市| 普格县|