蒼時弦也
蒼時弦也
資深軟體工程師
發表於

Tokenization 機制設計 - Clean Architecture in Go

這篇文章是 Clean Architecture in Go 系列的一部分,你可以透過 Leanpub 提前閱讀內容。

訂單的建立和查詢已經完成,然而下單者的名字仍然被明碼儲存在我們系統之中,我們希望針對個資做更好的保護,因此決定導入代號化(Tokenization)的機制,將原本儲存的名字已代號(Token)儲存,並且將真實的資料另外加密。

Tokenization

Tokenization 是一種資訊安全的技術,比起直接儲存明碼,或者在某個資料庫欄位上進行加密,可以有更好的安全性,以及更容易處理個資的刪除。

原理上並不複雜,我們只需要另外建立一個資料表(如:tokens)並且儲存代號以及對應的資料即可,在金流服務中,我們串接信用卡進行扣款時,銀行就會將卡號轉換成一個代號,外部的服務透過這個代號呼叫,就可以避免直接儲存明碼的信用卡卡號。

當我們將這個技術用在隱私相關的情境時,就可以達到去識別化的效果,在資料處理上都是某個代號,因此平常無法跟某個個資產生關聯,那麼就只需要保護好管理代號的資料表即可,用來避免個資散落在系統不同的地方。

設計功能

我們原有的建立、查詢訂單介面基本上已經確定下來,這表示我們加入代號化的處理不能破壞這個機制,那們我們該如何修改哪?

現有的系統中,我們有一個這樣的實體(Entity)

1type Order struct {
2	id    string
3	name  string
4	items []Item
5	state string
6}

現在,原本 name 儲存的是 Aotoki 的明碼,要轉換成像是這樣的代號格式 v1:de5c5369-19f2-4eaf-b5f0-7765619037de 在過往 MVC 的設計,我們可能會考慮以下選項

  • 在 Controller 收到請求時處理
  • 在 Model 插入額外的 Hook 處理

如果依照 Clean Architecture 的思考方式,在 Controller 處理是比較合理的,因為對 Model 來說,職責是管理狀態,要保存怎樣的資訊並不是關注的重點。

那麼,在我們現在的設計中,Use Case(使用案例)就很關鍵,因為我們可以在裡面描述一個「流程」中間具備一個代號化的機制。

擴充功能

要在 Use Case 中插入這個機制,實際上並不複雜,雖然我們會有其他問題需要考慮,然而在現階段可以先無視像資料庫的交易(Transaction)是否成功的問題,因此可以預期原本的 Place Order Use Case 會有這樣的調整。

 1// internal/usecase/place_order.go
 2
 3// ...
 4func (u *PlaceOrder) Execute(ctx context.Context, input *PlaceOrderInput) (*PlaceOrderOutput, error) {
 5	nameToken := tokens.New(
 6		uuid.NewString(),
 7		input.Name
 8	)
 9
10	err := u.tokens.Save(token)
11	// ...
12
13	order := orders.New(
14		uuid.NewString(),
15		nameToken.String(),
16	)
17
18	// ...
19
20	out := &PlaceOrderOutput{
21		Id:    order.Id(),
22		Name:  nameToken.Raw(),
23		Items: []PlaceOrderItem{},
24	}
25
26	// ...
27
28	return out, nil
29}

跟原本的版本相比,我們建立了一個叫做 nameToken 的實體,並且將它保存起來。同時將原本訂單的 name 改為 nameToken.String() 傳入,在輸出的部分則用 nameToken.Raw() 顯示原始的內容,讓 API 行為保持一致。

實際上修改並不複雜,我們接下來就開始著手重構這個功能讓個資保護的機制變得更完善。