Golang Session Note 0

Rain Wu
Golang 筆記
Published in
7 min readApr 21, 2019

昨天跑了個外出的行程,晚上回家後大概更新一下相關的資訊後大概就沒辦法很好的集中注意力了,所以拖到了週日才開始寫。預估不久後又會有新的任務要接,可能會再次壓縮我寫筆記的時間 (或者是睡眠時間?),也許是個檢視自己是否 work smart 的好機會~

接續上次那篇 Golang Cookie Note 之後,這次來寫和 Cookie 息息相關的東西,也就是 Session。

Session 的誕生

Cookie 的出現固然解決了大半問題,但仍有些缺陷,比如說她是個存在於客戶端的文件資料,很容易在 Client 端被拿來動手腳的。再者,Cookie 中若包含著機密的資料像是帳號密碼等等,每次發送請求都跟著出去一次,那被攔截或竊聽的風險可就大大提高了。因此便有了 Session 誕生的理由。

同樣是在 Client 端與 Server 端進行首次溝通 hand shaking 時,Server 端生成了一個 Session,可以把它視為一種維持兩端溝通的方案。若要把 Session 的精神發揮到極致,便是要考慮再完全沒有 Cookie 的情況下仍能維持 Session 機制的運作。

不過我想我本人對此沒有太過極端的堅持,畢竟我也不是什麼 Session 教派的鐵粉之類的,在設計 Session 機制時,好好善用 Cookie 來協助確實是可以解決一些頭痛的問題的。最簡單的例子就是在 Server 端生成 Session 時同時升成一串 Session id 隨著響應回覆到 Client 端,而下次 Client 端發起 Request 時就帶上這個 id 到 Server 匹配對應的 Session。

Golang 的標準庫中並沒有 Session 相關的 package,但自己刻輪子又太過勞民傷財了,所以這次會用 Gorilla 提供的 package 來實作,同時研讀一下他背後的原始碼究竟是如何寫的。為了避免讀者跟著實作到一半,就遇到 Undefine error 而提早下車,所以我先附上我會用到的 package:

encoding/gob
html/template
net/http
github.com/gorilla/mux
github.com/gorilla/securecookie
github.com/gorilla/sessions

首先先來做一些前置的處理,比如定義 User 資料結構、和幾個 Global var:

store 主要會成為接下來儲存 Session 資料的結構,而 tpl 主要用於協助 gohtml 模板檔案的轉換和執行。不過 template 的部分並非本文重點,所以還是先來看看 FilesystemStore 是個怎麼樣的結構吧~

開發者表示目前此儲存方式仍在試驗階段,歡迎各位路過的大大給回饋 OWO~ 它的內部主要是由一個 securecookie.Codec 的 slice 和 Options 結構指針以及一個字串路徑來構成,乍看之下貌似無法理解這三個東西發揮了什麼作用,先來聚焦在 securecookie.Codec 的 slice

可以看出這是一個包含對 Cookie 字串編碼及解碼功能的 interface,反推回去就能知道 []securecookie.Codec 就是一連串實現了指定編碼解碼功能的結構體。而下一個來關心一下 Options

很單純的就是個相關設定的結構體,檔案中看起來也沒時做任何方法。至於 path 的部分不太容易直接解釋,就等後續用到時再說。定義好需要用的結構和 Global Vars 之後,來做一下必要的初始化:

可以看到 init 中很大一部份仍是繞著初始化 Global Vars 在處理的,其中對 store 的初始化拿了兩把亂數生成的密鑰,值得深入看看

這邊的 comments 有提到 path 的用途,但 FilesystemStore 那部分的 comments 反而沒提到有點怪(?),總之你如果沒有講明的話,在 line 8~10 它會自動幫你找個地方暫時儲存 sessions,而 Options 則是生成時會有預設值,我們也可以後續再更動。

看起來最值得關注的便是他存放編碼解碼結構的 slice,來看看那兩組亂數密鑰被拿來做了什麼

這個 CodeFromPairs 的方法把獲得的 keyPairs 參數湊成倆倆一對,同時決定究竟要生產多少個實現 Codec interface 的結構體,並在 line 33 的部分用一個 New 方法帶上兩個 key 作為參數。

具體這個 New 內部做的就是生成一個 SecureCookie 結構體的指針,包含了大量編碼解碼過程,和文章主題相差有點大,同時我自己對編碼解碼也還沒太多研究怕寫起來不可靠,所以就先跳過了。

可以知道的是 SecureCookie 這個結構體確實是有隱含實現 Codec interface 的,因此在 line 33 回傳後可置入 slice 中,等所有要產出的 SecureCookie 都生成完成並置入 []codec 後就回傳給 FilesystemStore 的 Codec。

細心一點的話也許會留意到上面 NewFilesystemStore 方法中的 line 20 有一個 fs.MaxAge 的方法,看起來有點冗,不過他其實是有實際意義的:

透過 loop 的方式把對於 store 設置的有效時間同步更新到其中的所有 cookie 的有效時間,而 cookie 中資訊的主要用途就是匹配對應 session,等同是設置了所有 session 的作用時間。

另外 line 9 為了指定 sc 的結構體,所以用了 type assertion 的技巧,畢竟 codec 是個 interface,無法對他呼叫任何 interface 內容以外的方法的,所以需要明定究竟是哪一種實現他的結構體才能呼叫 MaxAge 方法。

回到 init 函數中的 line 17,gob 主要用於序列化編碼解碼,就是一種類似 json 的東西,當然也可以使用 json,但 json 的限制在於只能用 string 作為key,但 gob 能用 int,網路上很多把兩者拿來比較的文章,github 上也有不少神人拿了各種序列化編碼解碼的 package 做各方面的評測。

想要序列化就得明確指出它的結構長什麼樣子,Register 方法便是把我們自訂的結構給註冊進入序列化的可辨識結構名單內,不然的話她會不知道要以什麼樣的結構進行序列化編碼解碼,實測之後 compile 還是會過,但編碼解碼就真的會出事了。

init 函數中的 line 19 主要用於 parse template,畢竟 gohtml 檔案只是個方便 go 開發者快速產出的網頁模板工具,真的要執行還是要經過 parsing 的,template.Must 主要是用於做 error handling 的,而 ParseGlob 主要是 parsing 後把產出內容存入一個 template.Template 結構體並回傳。這部分和 Session 比較無關所以就簡略帶過了,不過後續確實需要 tpl 來執行 template 所以有空的話還是能看看他是在做什麼的。

一個不注意光是 initialization 就又洋洋灑灑的寫了一大堆了,是時候該管制一下篇幅暫時收手 XD,下次再繼續寫 ref 中剩下的部分,ref 的文章預設讀者已經懂很多東西,所以讀起來心有點累 Orz,我盡量用我自己的語言講的詳細一點。歡迎各位 golang 爐火純青的大大路過順便指點一下~

--

--

Rain Wu
Golang 筆記

A software engineer specializing in distributed systems and cloud services, desire to realize various imaginations of future life through technology.