閒談軟體設計:UUID 之三部曲

閱讀時間約 9 分鐘
這是 UUID 三部曲的最後一部,主要探討 UUID 的一些特殊應用,若想了解更多關於 UUID 的內容,可以參考 閒談軟體設計:UUID閒談軟體設計:再聊 UUID
駭客任務其實已經有四集了 XD

駭客任務其實已經有四集了 XD

背景

ㄟ~好啦,我知道離上一篇《閒談軟體設計:再聊 UUID》,沒多久再聊一次 UUID 有點怪,但這個使用情境其實挺有意思的,您是否有過以下情境:

  • 不想保存個資,苦於無法使用 email 之類的資料作為 ID
  • 網路爬蟲爬了一堆資料,卻不知道怎麼識別

其實上述的情境都能用雜湊 (Hash) 解決,例如,使用者用 abc@123.xyz 註冊一個帳號,在資料庫中可以用 SHA-1 的結果作為 ID:

17597a5e728f17ed90d9e3b5380596184d9d1146

有人可能會問,如果系統沒有儲存 email,那要怎麼實現忘記密碼這個功能 (重設密碼並將新密碼寄到 email 信箱)?這基本上不是個問題啊,因為要觸發忘記密碼這個功能,使用者就要先提供帳號,這時不就有 email 了,不需要從系統讀取 email 喔。

又或者是用爬蟲去爬銀行帳戶的資訊 (要先處理登入問題,不然什麼都爬不到),爬到一筆這樣的交易紀錄 (不用瞎猜了,這不是我的薪資,數字是亂編的)

這時我們可以將日期、摘要、單筆金額 (支出或存入)、結餘組成字串後,取 SHA-1 雜湊得到一組可以作為 ID 的字串,這好處是,下次再爬到同一筆交易紀錄,我們就可以知道這筆已經在我們的系統中了。

9ff6b42d95e1a84bfd20d49a093e4839bfa069b6

用雜湊的好處是無法回推原始的資料是什麼,或是嚴謹來說,是不容易回推原始的資料是什麼,雖然目前已知的 MD5、SHA-1 都已經被找到可在合理時間內計算出碰撞的演算法,但畢竟我們是拿雜湊值作為 ID,而不是更敏感的密碼比對,以上述的例子來說,交易紀錄則是原本就會儲存在系統中的,從雜湊回推有點多此一舉,比較糟糕的情境,應該是原本不希望曝光的 email 被用碰撞的方式曝光了。

UUID 特殊用法

那... 不就解決了,跟 UUID 有什麼關係?在首部曲 閒談軟體設計:UUID 有提到:

若看 Wikipedia 上對 UUID 的描述可以知道 UUID 目前有 5 個版本,詳細的產生方式可以在 RFC 4122 文件的第四節找到,其中版本 3 和版本 5 比較特殊,是以名字產生 UUID,差別是以 MD5 演算法或 SHA-1 進行 hash,在特定條件下,每次產生的 UUID 都應該是一樣的 (用以識別是否為相同節點),比較不適合用在我們討論的情境中。

當時的情境用不到 v3 或 v5,但現在的情境就非常合適用 v3 或 v5 了,因為我們希望的是對同一筆資料,不管是 email 或是交易紀錄,都可以產生同樣的 UUID。而實際上,Java 原生就有提供 UUID v3 的生成方式,給定一位元陣列,該函式會用 MD5 的方式取雜湊值,然後將版號設為 3 (用 &= 0x0f 保留第六個 byte 的低位四個 bits,清除高位四個 bits,並用 |= 0x30 設定為 3),並將 variant 設為 2 (用 &= 0x3f 保留低位六個 bits,清除高位兩個 bits,用 |= 0x80 設定為 2),結果就是一個 v3 的 UUID 了。

也就是說,v3 的 UUID 可以視作修改後的 MD5,要回推更是不可能了,因為雜湊值在設定 version 及 variant 的過程中已經遺失部分資訊了,以上述的例子來說,是更適合的 ID 產生方式。

可惜的是,Java 原生並沒有 UUID v5 的生成方式,但有了上述的範例,要產生 UUID v5 並不難,首先先定義介面,這裡參考一個 JavaScript 套件 uuid 的設計。

第一個參數是真正用來產生 ID 的字串,第二個參數是 namespace,預設是空字串,第三個參數可以決定是否偽裝成 UUID v4,第三個參數後面再來說,先說第二個參數。

namespace 其實蠻好用的,例如上述的交易紀錄的例子,如果好死不死,交易日期一樣 (很多銀行的網頁只提供日期,沒有交易 ID 與時間),單筆金額一樣,餘額也一樣,那以剛剛的方式會產生相同的雜湊,此時,不同的銀行用不同的 namespace (更好的 namespace 是帳號),就會產生不同的雜湊了。

此 uuid 套件是完全照 RFC 4122 的 4-3 節所描述的,用附錄 C 預先指定的 UUID 作為 namespace,也就是

  • 若 name 是網域,namespace 是 6ba7b810–9dad-11d1–80b4–00c04fd430c8
  • 若 name 是網址,namepsace 是 6ba7b811–9dad-11d1–80b4–00c04fd430c8 (差異在第一節的最後一個數字)

另外還有定義 ISO OID 及 x.500 DN 所用的 namepsace,但我過去在使用這套件覺得有點麻煩,為了產生 v3 或 v5 的 UUID,得再產生一個 UUID 作為 namespace。故這次介面的設計,沒有將第二個參數的型別定義成 UUID,只要演算法的結果不違反 4-3 節的幾點要求,我覺得就算 ok

  • 不論時間,在同個 namespace 底下同個 name 所產生的 UUID 必相同
  • 同個 namespace 底下不同 name 所產生的 UUID 需不同 (機率上來說)
  • 同個 name 在不同的 namespace 所產生的 UUID 需不同 (機率上來說)
  • 兩個相同的 UUID,應該來自同個 namespace 底下的同個 name (機率上來說)

先看第一個版本,這版本有點想得太簡單了,將 name 及 namespace 串起來算 SHA-1,設定 version 與 variant 後產生取高位的 128 bits 作為 UUID,但這個版本有問題,先想想看問題是什麼?等等再往後看答案吧!

這邊要注意的是 MD5 雜湊值長度為 128 bits,跟 UUID 在長度上剛好一樣,沒什麼問題,但 SHA-1 雜湊值長度是 160 bits,所以得思考怎麼取 128 bits 出來,這裡取高位元是因為,我印象中高位元的的亂數分布比較好 (別問我數學證明,因為我也不確定是否真是如此)。

先檢驗一下,由於是用 SHA-1 計算雜湊,也沒有時間戳記或亂數,不管怎麼算,都不受時間影響,滿足第一點。不同的 name 在同個 namespace 底下,串起來的字串就會不一樣,基本上得到相同的雜湊值機率極低,因此滿足第二及第三點。但卻無法滿足第四點,很明顯也很容易發現,"abc@123.xyz" 加 "" 和 "abc" 加 "@123.xyz" 會產生相同的 UUID,卻來自不同的 name 與 namespace:

所以第二個版本,修改不大,個別先取 name 與 namespace 的 SHA-1 後,合在一起再取一次 SHA-1,但效果很明顯。

從測試案例可以看到,計算出來的結果天差地遠,卻仍能夠滿足第一點到第四點的要求:

總結

就這樣,我們有一個可以簡單產生 UUID v5 的工具。那回頭看一下第三個參數,到目前為止,我們都是乖乖牌,按照 RFC 4122 標準,將 version 設為 5,但若不想讓看到的人知道這是從 name 和 namespace 產生出來的呢?那就只好偽裝了,所以第三個參數就是提供偽裝成 v4 的功能。

雖然說第二個版本是 name 與 namespace 先取一次 SHA-1 後,合起來再取一次 SHA-1,理論上來說要產生碰撞會比單取一次 SHA-1 要難上許多,加上已經將 version 與 variant 所在的 bits 破壞掉了,讓難度再往上提升,可是如果在一開始就讓人誤以為這個 UUID 是純粹亂數產生的 UUID v4,自然可以降低一些人想要嘗試碰撞的機會。

當然,如果搭配閒談軟體設計:再聊 UUID使用,不僅可以有更短的表示法,也不容易猜出是 UUID v5 了,好啦!UUID 的第三部曲就到此結束,至於會不會再有第四部曲?我覺得是很難了...


後記:這裡要抱怨一下 Kotlin 的設計,我知道 static 變數是不好的東西,但直接從語言中把所有 static 移除是不是太過了些,然後為了一些明明用 static 就很容易解決的情境,加一個很不直覺的 companion object,以這次的 UUID 為例,若用 Swift 的 extension 來完成,整個就很漂亮。

在使用上,可以延續 UUID 的類別名稱,不用另外加一個 NamedUUID 類別 (Kotlin 並未替所有的 Java 原生類別都加上預設的 companion object,所以無法用 extension 的方式替 UUID 加上 factory method)。



對了,本文的 NamedUUID 可以在這個 repo 找到完整的程式碼 (懶得再開一個新的,所以就沿用 short-uuid 了):


avatar-img
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
UUID 有個致命的缺點是,它實在太長了,128 bits 用 Hex 表示法,至少要 32 個字元,如果再加上分隔符號,就要 36 個字元,把這放在面向使用者者的頁面上,應該不會有人會記得住吧!但 UUID 就真的只能這麼長嗎?其實是可以再短一點的。
這是 UUID 三部曲的首部曲,接下來兩周陸續更新第二部與第三部,首部曲先聊聊 UUID 的前世今生,二部曲會聊聊如何縮短 UUID 的長度,第三部曲則是聊聊特殊應用,希望能幫助到大家。
在大型主機的年代 (這裡要澄清一下,本人沒有歷經那個年代,不然都把自己講老了),就已經有 clent server 的概念:由 client (計算能力較差的終端機) 向 server (計算能力較好的大型主機) 請求服務,幫助理解與管理分散式系統程式的複雜度。
每次看到 REST 就讓我想起以前念研究所和老師一起想論文題目時,曾提過國外常會玩這種文字遊戲,像是將 Representational State Transfer 變成一個很簡單的單字 REST,但我的東西不管怎麼想,卻想不出什麼有趣的東西 Orz
稍微複雜一點的 query 其實代表著某些商業邏輯,若把這一段程式放到 repository 的實作層,會變成這些商業邏輯被隱藏起來了,如果有個好的描述語言,我倒覺得很好讀,也可以清楚知道背後的商業邏輯是什麼,是很好的一件事。
簡單說,Repository 提供像是 array 或是 dictionary 的容器,對程式來說,就好像記憶體中有所有的物件,不用去想物件其實是從資料庫來的。作法是在 data mapping layer 之上再提供一個 collection 的介面來存取物件,將資料庫的細節從商業邏輯層抽離。
UUID 有個致命的缺點是,它實在太長了,128 bits 用 Hex 表示法,至少要 32 個字元,如果再加上分隔符號,就要 36 個字元,把這放在面向使用者者的頁面上,應該不會有人會記得住吧!但 UUID 就真的只能這麼長嗎?其實是可以再短一點的。
這是 UUID 三部曲的首部曲,接下來兩周陸續更新第二部與第三部,首部曲先聊聊 UUID 的前世今生,二部曲會聊聊如何縮短 UUID 的長度,第三部曲則是聊聊特殊應用,希望能幫助到大家。
在大型主機的年代 (這裡要澄清一下,本人沒有歷經那個年代,不然都把自己講老了),就已經有 clent server 的概念:由 client (計算能力較差的終端機) 向 server (計算能力較好的大型主機) 請求服務,幫助理解與管理分散式系統程式的複雜度。
每次看到 REST 就讓我想起以前念研究所和老師一起想論文題目時,曾提過國外常會玩這種文字遊戲,像是將 Representational State Transfer 變成一個很簡單的單字 REST,但我的東西不管怎麼想,卻想不出什麼有趣的東西 Orz
稍微複雜一點的 query 其實代表著某些商業邏輯,若把這一段程式放到 repository 的實作層,會變成這些商業邏輯被隱藏起來了,如果有個好的描述語言,我倒覺得很好讀,也可以清楚知道背後的商業邏輯是什麼,是很好的一件事。
簡單說,Repository 提供像是 array 或是 dictionary 的容器,對程式來說,就好像記憶體中有所有的物件,不用去想物件其實是從資料庫來的。作法是在 data mapping layer 之上再提供一個 collection 的介面來存取物件,將資料庫的細節從商業邏輯層抽離。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
......究其根本動畫版與漫畫版的癥結點是相同的,也就是配角人數過多使得情節處理難以做到盡善盡美,其中又以漫畫版的問題更為明顯......
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
前幾年因為身體的關係,當了幾年的律師逃兵,當時開了之前的事務所以後,一時間也沒有特別想要做甚麼事情,所以就邊讀一點書、早晚運動一下,剛好聽到當年同梯朋友進去金融業工作,因此也抱著嘗(ㄊㄠˊ)試(ㄅㄧˋ)的心態,找了份銀行法令遵循的工作
Thumbnail
那天我問隊友,怎樣才算是一部小說呢?按字數計算嗎? 他說:「故事內起承轉合都有,就算」 所以我大膽地按著他的標準,將自己寫過的故事,粗略整理出一個明細。
纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
Thumbnail
婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
......究其根本動畫版與漫畫版的癥結點是相同的,也就是配角人數過多使得情節處理難以做到盡善盡美,其中又以漫畫版的問題更為明顯......
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
前幾年因為身體的關係,當了幾年的律師逃兵,當時開了之前的事務所以後,一時間也沒有特別想要做甚麼事情,所以就邊讀一點書、早晚運動一下,剛好聽到當年同梯朋友進去金融業工作,因此也抱著嘗(ㄊㄠˊ)試(ㄅㄧˋ)的心態,找了份銀行法令遵循的工作
Thumbnail
那天我問隊友,怎樣才算是一部小說呢?按字數計算嗎? 他說:「故事內起承轉合都有,就算」 所以我大膽地按著他的標準,將自己寫過的故事,粗略整理出一個明細。
纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
Thumbnail
婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。