聊技術(shù),不止于技術(shù)
應(yīng)用拆分微服務(wù)后,一個不可避免的問題就是權(quán)限問題。拆分后的各個微服務(wù)如何處理權(quán)限,怎么處理才能保證滿足業(yè)務(wù)的需求,怎么處理才能保持架構(gòu)的簡單及可維護?今天的文章,讓我們來深入微服務(wù)架構(gòu)下的權(quán)限處理問題,看看這個沒有最佳實踐的領(lǐng)域,如何能夠針對業(yè)務(wù)需求來設(shè)計的較為優(yōu)雅。
1
先來理解幾個名詞關(guān)于權(quán)限,可能有很多相關(guān)名詞,而且這些名詞也很可能會讓大家混淆或是疑惑(為了避免讓大家產(chǎn)生疑惑,我就不列舉這些名詞了)。但本文中會用到幾個核心的有關(guān)權(quán)限的詞,理解這幾個詞對于理解本文必不可少,所以關(guān)于權(quán)限處理至少有下述三個名詞需要大家先來理解。(1)認證。(2)鑒權(quán)。(3)授權(quán)。先來說說認證。我們都會用到各種各樣的系統(tǒng),并且在進入系統(tǒng)之前都需要你輸入用戶名/密碼,點擊登錄后,后臺做的事就是認證,及驗證你的用戶名/密碼是否正確,能否登錄系統(tǒng)。這就是認證做的事。再來說說鑒權(quán)。同樣的系統(tǒng),不同的人登錄成功后,擁有的權(quán)限是不一樣的,在你進行操作時,后臺會驗證你是否能夠進行相應(yīng)的操作,這就是鑒權(quán),及校驗用戶是否有相應(yīng)資源的操作權(quán)限。最后來說說授權(quán)。不同的用戶擁有不同的權(quán)限是通過系統(tǒng)授權(quán)來實現(xiàn)的,授權(quán)就是授予相關(guān)用戶或角色資源操作的權(quán)限。當然授權(quán)可能還包含對第三方系統(tǒng)的授權(quán),本文暫不討論此類場景。其實對于大多數(shù)應(yīng)用系統(tǒng)來說,權(quán)限處理最關(guān)鍵的就是要解決上述三個詞所描述的問題,即系統(tǒng)權(quán)限處理要做的事就是認證、鑒權(quán)和授權(quán)。
2
認證、鑒權(quán)和授權(quán),在單體應(yīng)用中如何實現(xiàn)?在上文中,我們說明了權(quán)限問題的核心就是解決認證、鑒權(quán)和授權(quán)問題。想要知道微服務(wù)架構(gòu)中如何處理權(quán)限問題,不如讓我們先看看在單體應(yīng)用中是如何處理上述問題的?讓我們來分別說明。(1)認證單體應(yīng)用中,對于用戶登錄,會先校驗用戶的登錄信息(用戶名/密碼),這其中可能會涉及到密碼的加密處理,一般的判斷是加密后相應(yīng)的密碼是否與數(shù)據(jù)庫中存儲的密碼相等,如果相等的話,則登錄成功。用戶登錄成功的話,一般會返回用戶登錄成功相關(guān)憑證。如果是JWT的話,則會返回Token,如果采用的是會話的話,則會通過Set-Cookie返回SessionId給客戶端。(2)鑒權(quán)單體應(yīng)用一般會通過攔截器(Spring Security、Apache Shrio本質(zhì)上都是攔截器),攔截用戶請求,這里同樣會根據(jù)鑒權(quán)方案是JWT還是HTTP會話分別處理,如果是JWT的話,則會通過解密來獲得用戶信息,如果是采用的會話的方式,則會根據(jù)會話ID,從存儲中(這里一般是Redis)來獲得用戶信息,不論是哪種方案,最終都是根據(jù)用戶信息來對相應(yīng)的請求進行權(quán)限校驗。這里需要注意的是,鑒權(quán)一般有兩種方式,但都是基于角色的。一種是基于角色的隱式鑒權(quán),即根據(jù)角色直接判斷是否擁有相應(yīng)資源的操作權(quán)限,比如你的角色是管理員,你就可以刪除用戶,你的角色是普通用戶,你只能查看用戶信息。這種一般在簡單的系統(tǒng)中比較適用,常用的方式是通過注解,表明哪個接口可以給哪個角色訪問。但這種方式在復雜的系統(tǒng)中就會變得難以維護。另一種是基于角色的精準鑒權(quán),此種鑒權(quán)方案,一般會給角色分配明確的權(quán)限,相應(yīng)的鑒權(quán)方式為根據(jù)用戶角色查出具體的權(quán)限集合,然后進行進一步判斷。此種方式在復雜系統(tǒng)中更加有效方便。(3)授權(quán)在單體應(yīng)用中,授權(quán)即修改用戶相關(guān)角色信息,或者修改角色相關(guān)權(quán)限信息。一般在用戶重新登錄后,最新的權(quán)限信息生效。
3
在微服務(wù)架構(gòu)下如何實現(xiàn)上述功能?其實在微服務(wù)中,權(quán)限處理的認證、授權(quán)功能實現(xiàn),跟單體應(yīng)用沒什么區(qū)別。從微服務(wù)的基于業(yè)務(wù)拆分的原則上,對于用戶相關(guān)的業(yè)務(wù),我們都會單獨創(chuàng)建一個用戶服務(wù),用戶服務(wù)作為一個單獨的微服務(wù),在實現(xiàn)認證、授權(quán)功能時,和單體應(yīng)用保持一致即可,即相應(yīng)的用戶登錄信息校驗、用戶角色修改、角色的權(quán)限修改,在用戶服務(wù)中獨立實現(xiàn)即可。所以在微服務(wù)架構(gòu)下權(quán)限處理唯一與單體應(yīng)用不同的便是鑒權(quán)方式。鑒權(quán)方式為什么變得不同了呢?因為鑒權(quán)不再集中在單體應(yīng)用中了,鑒權(quán)被分散在了各個微服務(wù)中。所以現(xiàn)在問題的關(guān)鍵就變成了從哪里獲取用戶相關(guān)信息,然后在哪里進行鑒權(quán)。下面讓我們來看幾種微服務(wù)架構(gòu)下的鑒權(quán)方案:第一種方案為,從用戶服務(wù)獲取用戶信息,然后各個微服務(wù)分別鑒權(quán)。如下圖所示:
上圖中有三個微服務(wù),分別是倉儲服務(wù)、訂單服務(wù)、以及用戶服務(wù),對于每個微服務(wù)的訪問都需要進行鑒權(quán),相應(yīng)的鑒權(quán)方式為,所有對微服務(wù)的訪問在經(jīng)過網(wǎng)關(guān)后都統(tǒng)一先訪問用戶服務(wù)獲取用戶相關(guān)信息,包括角色信息、角色權(quán)限信息,然后分別在各個微服務(wù)中進行鑒權(quán),判斷用戶是否有權(quán)限進行相應(yīng)的操作。上述方式的優(yōu)點是實現(xiàn)簡單,對比單體應(yīng)用的實現(xiàn)來說,只是換了個地方獲取用戶信息,權(quán)限相關(guān)的判斷還是保留在各個微服務(wù)中。對應(yīng)的缺點也很明顯,用戶服務(wù)需要保證高性能、高可靠以及可擴展,并且隨著服務(wù)的增多,每個服務(wù)都需要與用戶服務(wù)交互,服務(wù)間調(diào)用可能會顯得略加繁瑣,不夠簡單優(yōu)雅,同時每個微服務(wù)中的權(quán)限判斷邏輯也都重復冗余。綜上,個人并不是很推薦這種方式。其實對于第一種方案,我們略作修改,就可以得到一個略微優(yōu)雅的方案,如下圖,看第二種方案:
第二種方案為從用戶服務(wù)獲取用戶信息,然后在網(wǎng)關(guān)進行鑒權(quán),這樣整個架構(gòu)看起來是不是就清爽了許多?(美好的東西總是看起來簡單的)大家可能看到,這里將請求往具體的微服務(wù)透傳的時候,帶上了用戶信息,這個用戶信息可以放在HTTP Header中。為什么透傳的時候要帶上用戶信息呢?因為后端具體的微服務(wù)業(yè)務(wù)可能需要獲取當前用戶的信息,但這里并不是用來鑒權(quán),只是業(yè)務(wù)邏輯中用到了而已。第二種方式的優(yōu)點是架構(gòu)簡單,對于鑒權(quán)來講服務(wù)間調(diào)用交互更加簡潔,并且后端微服務(wù)不需要冗余鑒權(quán)業(yè)務(wù)邏輯。缺點同樣是用戶服務(wù)需要保證高性能、高可靠以及可擴展,并且鑒權(quán)邏輯需要在網(wǎng)關(guān)實現(xiàn)。微服務(wù)的理想狀態(tài)是高內(nèi)聚低耦合,每個服務(wù)專注于自己的事,各個服務(wù)間功能獨立,獨立演進,所以針對微服務(wù)鑒權(quán)還有沒有更好的方案?請看第三種方案:
鑒權(quán)要做的事是判斷用戶有沒有相應(yīng)資源的操作權(quán)限,所以從微服務(wù)的基于業(yè)務(wù)拆分的原則基礎(chǔ)上,鑒權(quán)放在用戶服務(wù)是不是更合適?所以上述方案將鑒權(quán)放在了用戶服務(wù)來實現(xiàn),我們不再需要調(diào)用用戶服務(wù)來獲取用戶信息然后進行鑒權(quán)了,直接將用戶的請求轉(zhuǎn)發(fā)到用戶服務(wù),在用戶服務(wù)中統(tǒng)一處理鑒權(quán)。此種方案下網(wǎng)關(guān)要做的事也將更加專注,只做請求的轉(zhuǎn)發(fā)即可,后端各個微服務(wù)也只需專注于自己的業(yè)務(wù)即可。此種方案,在我看來是比較推薦的方案,并且應(yīng)該能滿足微服務(wù)架構(gòu)下大多數(shù)系統(tǒng)的權(quán)限處理需求。當然缺點是對用戶服務(wù)的高性能、高可靠以及可擴展提出了更高的要求。除了上述三種鑒權(quán)方式,微服務(wù)架構(gòu)下可能還有其他鑒權(quán)方式,比如共享用戶信息緩存的方式,這種方式下,各個微服務(wù)統(tǒng)一從緩存獲取用戶信息,然后分別在各自進行鑒權(quán)?;诖朔N方式也很容易想到一個優(yōu)化的變種,即從網(wǎng)關(guān)獲取緩存的用戶信息,然后在網(wǎng)關(guān)進行鑒權(quán)。諸如此類的鑒權(quán)方式,我個人是不太推薦的,當然采用此類鑒權(quán)方式的團隊可能也有他們的考慮,更多的可能是出于性能的考慮。至于我為什么不推薦諸如此類的方式呢,我覺得我們在做系統(tǒng)架構(gòu)的時候需要有自己的原則,其實對于很多問題我們可以有許多種解決方法,哪種方案比哪種好可能并不好說,甚至團隊在方案的選擇上還會有所爭執(zhí),但如果在架構(gòu)設(shè)計上團隊有自己的指導原則的話,可能能夠幫助我們在方案的選擇上達成共識。個人在架構(gòu)設(shè)計上的原則包括但不限于可維護原則、可擴展原則、簡單性原則等,所以與其說此類方案不好,倒不如說這并不符合我的設(shè)計原則。寫在最后應(yīng)用拆分微服務(wù)后,一個不可避免的問題是權(quán)限問題。權(quán)限問題要解決的核心問題是認證、鑒權(quán)和授權(quán)。在說明微服務(wù)架構(gòu)下如何處理權(quán)限問題之前,我們先看了單體應(yīng)用下是如何處理權(quán)限問題的認證、鑒權(quán)和授權(quán)的。對于微服務(wù)來講,權(quán)限問題的處理與單體應(yīng)用中的處理最大的不同是鑒權(quán)的方式,至于認證、授權(quán)的實現(xiàn)與單體應(yīng)用并無太大不同,在微服務(wù)中的用戶服務(wù)下實現(xiàn)即可。微服務(wù)下鑒權(quán)處理為什么變得不同了呢?因為鑒權(quán)不再集中在單體應(yīng)用中了,鑒權(quán)被分散在了各個微服務(wù)中。所以問題的關(guān)鍵是微服務(wù)下該從哪里獲取用戶相關(guān)信息,然后在哪里進行鑒權(quán)。我們詳細介紹了微服務(wù)架構(gòu)下三種鑒權(quán)的實現(xiàn),第一種方式是從用戶服務(wù)獲取用戶信息,然后在各個微服務(wù)中鑒權(quán)。第二種方式是從用戶服務(wù)獲取用戶信息,然后在網(wǎng)關(guān)統(tǒng)一鑒權(quán)。第三種方式不再需要獲取用戶信息,直接通過網(wǎng)關(guān)轉(zhuǎn)發(fā)請求,在用戶服務(wù)進行統(tǒng)一鑒權(quán),各個服務(wù)的職責劃分更加明確,也是個人推薦的實現(xiàn)方式。除此之外,還介紹了一些其他的鑒權(quán)實現(xiàn)方案,問題的解決方案并不唯一,有時候也很難說出哪種更好,我們要有自己的設(shè)計原則。
聊技術(shù),不止于技術(shù)。
在這里我會分享技術(shù)文章、管理知識以及個人的思想感悟,歡迎點擊關(guān)注。