[Golang] Build A Simple Web Service part.6 — Sign up, Login & Logout with React.js
Introduction
這篇的學習目標是Golang的註冊,登入及登出。
過程中會先複習到Cookie,Session,Web storage的特性; 前端將使用React開發並用上SPA,以及用AJAX在操作cookie應注意的事情,後端仍用Golang; 最後串起在註冊,登入及登出雙方向的互動。
實作將會以[Golang]Build A Simple Web Service系列以及[React.js] Build An React App系列為基礎,依舊將API server跟web page server分開,以保留開發時的自由度,因此在cookie的操作上將是cross domain的。
Cookie
HTTP的特性之一是stateless(無狀態),網景公司發明cookie繞過無狀態而使HTTP更好使用,但也造成了一些問題。
讓我們回顧cookie在瀏覽器中的特性如下:
1.client端的資料有變動時,藉由寫入cookie可以知道使用者做了哪些動作。
2.cookie是用domain name區分 (domain specific),在任何時機點只要對該domain的server發送request,對應的cookie會被一併夾帶到server。
3.cookie在HTTP中是明文傳送,有安全性的疑慮。
4.cookie的形式為key-value pair,並可以設定expired time以指定其存活時間。
當server收到帶有cookie的request時將會將帶有cookie的response回去。因此也可以在server side為瀏覽器設定cookie。
Session
session是server side儲存使用者資訊的機制。
剛才有提到,server side有能力為瀏覽器設定cookie。使用場景是server會將使用者的uuid放到cookie中,因此如果使用者曾經登入過,當傳到server時,若cookie中的uuid在session中確實有對應的使用者,則可以視為已登入過。
以下是session儲存的地方以及其優缺點:
1.內存: 可能會造成memory leak。
2.緩存: 是實務上最常採用,像是Redis,Memcache。
3.cookie: cookie會隨之變大,而導致request的資料量變大。
4.Database: 可能會造成讀取相當頻繁。
Other storage: Local storage and Session storage
除了cookie外,可以儲存client端資料的還有 web storage,這是HTML5時加入的新技術,可細分為local storage及session storage兩種,三者差異如下:
1.cookie: 大小大約4kb,關閉瀏覽器後會被清除,client side跟server side都可以設定cookie。
2.local storage: 大小大約5MB,將會被永久保存,需要被清除(explicitly deleted),但僅存在於client side中,不參與跟server side的通訊。
3.session storage:大小大約5MB,關閉瀏覽器或是頁面都會清除,但僅存在於client side中,不參與跟server side的通訊。
用cookie可以追蹤使用者的行為,然而現在歐洲國家有GDPR使用cookie時都必須要提示consent給使用者,使用者也必須同意。 web storage由於無法參與與server side的通訊,因此無法追蹤使用者的行為。
workflow
接著要說明的是web page server與API server的workflow,以Sign up為例:
- Web page的行為:
使用者訪問/signup頁面並送出使用者資料時,頁面會透過AJAX呼叫 API server的/signup endpoint,設定session並儲存使用者資料,會再透過AJAX呼叫call API server的/user endpoint以獲得使用者資料,並更新狀態React component的state,此時會重新rendering頁面,用User component取代Signup component。 - API server的行為:
1.檢查使用者的名字是否被註冊過了。
2.建立新的Session及替clien side設定cookie。
3.將使用者資訊存到database (這裡是用內存當範例)。
Login的workflow:
- Web page的行為:
使用者訪問/login頁面並送出資料時,頁面會透過AJAX呼叫 API server的/login endpoint,設定session並儲存使用者資料,會再透過AJAX呼叫call API server的/user endpoint以獲得使用者資料,並更新狀態React component的state,此時會重新rendering頁面,用User component取代Signup component。 - API server的行為:
1.檢查使用者的名字是否被註冊過了。
2.建立新的Session及替clienside設定cookie。
Logout則是清除sessin及使用者的cookie。
Implementation
Web page server and React components
Web page server的架構是延續[React.js] Build An React App part.2- Material-UI, SPA and React Router. web page server為SPA,採用Node.js,僅提供一個entry point且有三個頁面:
1. /Sign up: 提供新註冊用的表單
2. /Login: 提供登入用的表單。
3. /User: 登入成功後跳轉的頁面 + Log out按鈕。
檔案結構將會如左圖所示, 多了Register.js,Login.js及User.js。
Main page component
要注意的事有:
- 用react-dom-router的Route來控制路由,並用render來加入條件判斷,並在child components裡更新parent component的state.hasLogin。
- 在componentDidMount時檢查cookie,有cookie則詢問API server此使用者是註冊過,若有則親自改寫state.hasLogin (使用js-cookie)。
MainPage.js的程式碼如下:
Fetch in React
關於React中使用Fetch使用方法,先前在此篇文章已經有簡單介紹過: [Golang] Build A Simple Web Service part.5 — Communicate with the ReactJS page, 這裡再記錄一些細節。
Fetch的Request參數比較重要的是mode跟credentials,mode可分為same-origin/cors/cors-with-forced-preflight/no-cors; 而credentials可分為include/omit/same-origin。
Fetch的Response類型也有分成三種, 分別是basic/cors/opaque,basic可以拿到response的所有資料,cors除了Cache-Control,Content-Language,Content-Type,Expires,Last-Modified和 Progma之外的資料都拿不到,如果是opaque則基本上拿不到任何有用的資料。
在跨域操作Cookie時,需要將mode設為no-cors及將credentials設為include,而這樣得到的Response類型會是opaque。
Sub page component
需要新增Register.js,Login.js及User.js。
Register.js的程式碼:
Login.js的程式碼:
User.js的程式碼:
API server
API server的架構是延續[Golang] Build A Simple Web Service part.5 — Communicate with the ReactJS page的專案。API server提供3個end point:
1. /Sign up: 新增新的session並將使用者資料寫到database (或內存)
2. /Login
3. /Logout
新用到的package將會有:
- satori/go.uuid: 以製作新的session ID。
- crypto/bcrypt: 新使用者註冊時用來加密password, 並在登入時驗證使用。
程式碼只要修改routes/webService.go的內容,此範例用內存代替database以簡化程式碼,並新增兩個global variable: dbSessions以存放當前的session,dbUsers存放註冊過的使用者資料。
getUser()會回傳JSON格式的資料,將Golang struct封裝成JSON格式回傳有兩種方式,一種是用json.Marshall, 寫法如下:
第二種是用json.NewEncoder.encode,也是這次的範例所採用的方式,routes/webService.go的程式碼如下:
Summary
下圖為Sign up的頁面(使用者登入前):
使用者登入後的頁面:
在Golang及React都有一些累積後,逐漸進到前後端互動的操作,登入,登出,註冊,這篇牽涉到的範圍有點廣,盡可能地將這篇整理地收斂一些,若有不清楚的地方建議您可以參考Related topics或是Reference。
謝謝你耐心地讀到Summary,我是Sean HS, 是位軟體工程師。
這片文章是我在研究過程時的筆記,若有錯誤之處,期待您的見解,與您交流討論。
Related topics
Introduction To SaaS for APM & Logging
[Golang] Build A Simple Web Service:
[React.js] Build An React App:
- gorilla/sessions 学习笔记
- Go实战 — Gorilla web toolkit使用之gorilla/sessions(iris+sessions)
- 介紹 Session 及 Cookie 兩者的差別說明
- React-router-dom: Redirect
- React-cookie
- js-cookie
- 使用 Fetch 發送請求 ( request )區段
- Pass props to a component rendered by React Router
- CORS requests with session/cookie
- Fetch进阶指南
- Golang Response Snippets: JSON, XML and more