侯海平
安徽財貿職業(yè)學院 信息工程學院,安徽 合肥 230061
線上業(yè)務的處理能力越來越受到企業(yè)關注,大量線下業(yè)務都逐步遷移到線上進行處理,微服務架構也成為在線流量大、并發(fā)性高業(yè)務場景中的必選架構,大量業(yè)務系統(tǒng)由單體架構向微服務架構遷移[1]。無論是單體架構系統(tǒng)還是微服務架構系統(tǒng)都需要對業(yè)務資源實施授權訪問機制,防止不具備權限的用戶賬號訪問不匹配的內容資源。微服務架構下系統(tǒng)資源分散、系統(tǒng)開發(fā)技術多樣化、系統(tǒng)模塊相對獨立給授權認證帶來困難。
目前應用領域還存在大量的單體應用,例如一些傳統(tǒng)的業(yè)務系統(tǒng)如OA、財務系統(tǒng)、圖書借閱系統(tǒng)等,它們在用戶數(shù)量相對有限和穩(wěn)定的情況下依然能夠滿足業(yè)務運營的日常需求,因此依然有存在和使用的價值。所謂單體架構是指所有的業(yè)務系統(tǒng)功能都集成在一個開發(fā)項目中,這些業(yè)務代碼最終將被打包到一個程序中,該程序運行在操作系統(tǒng)中以一個進程為載體運行。例如Java 項目會使用Maven包管理工具將項目打包成一個war 包或一個jar包,然后以一個進程的形式運行在Tomcat 服務器程序中或直接運行在Java 虛擬機中。如果是.net(微軟平臺下)項目則會以一個dll 文件作為項目入口,運行在IIS 服務器程序中。通常這些單體架構只會映射到一個IP 和端口上,對外界提供服務。
在單體架構中,驗證用戶賬號、密碼的組件與鑒別用戶身份的組件,以及與提供受限資源的組件都是在同一個Web 程序中,因此只需考慮同一用戶同一session 會話周期即可。一次鑒權過程主要包括2 個主要活動:第一,用戶提交賬號和密碼登錄,服務驗證賬號和密碼;第二,用戶成功登錄后,訪問受限資源,服務器鑒權后提供受限資源給用戶。
認證活動的步驟:
步驟1:用戶在瀏覽器上輸入賬號和密碼提交至服務器進行驗證;
步驟2:服務器收到用戶請求的賬號和密碼,對其進行驗證;
步驟3:服務器驗證通過后,將建立屬于該用戶的session 會話,將向session 中寫入授權憑證;
步驟4:服務器向用戶返回登錄成功的消息,并向用戶所在瀏覽器的cookie 寫入與服務器一致的sessionid。
鑒權活動的步驟:
步驟5:用戶在登錄后,向服務器發(fā)起訪問受限資源的請求;
步驟6:服務器根據(jù)用戶提交的cookie 中sessionid 辨認用戶身份,通過之前存放的session 憑證,來驗證用戶權限的合法性;
步驟7:服務器鑒權通過后,將用戶需要的受限資源返回給用戶,流程結束。
可以看到整個流程中,服務器要使用session 會話對象,只要有新的用戶進來,服務器就要創(chuàng)建新的session 對象,并且對通過驗證的用戶在session對象中存入身份憑證,這些數(shù)據(jù)都保存在服務器內存中。整個服務器對session 對象的管理建立過期機制,用戶如果長時間不訪問服務器,服務器將自動銷毀該用戶session 對象,如果用戶一直保持訪問服務器,服務器將不斷延長session 對象的生命周期。
服務器要想識別出不同用戶,依靠的是用戶瀏覽器的cookie 對象,用戶每次訪問服務器時,瀏覽器都會自動將客戶端cookie 主動提交至服務器。服務器通過cookie 中的sessionid 找出用戶,根據(jù)用戶找出對應的憑證,再根據(jù)憑證確定用戶是否具備訪問某資源的合法性。
隨著業(yè)務流量持續(xù)上升,這些單體架構系統(tǒng)需要進行集群化部署。使用同一個域名指向多個不同服務器地址,一般可以采用Nginx 反向代理架構,實現(xiàn)多臺服務器對外提供服務。例如一臺服務器可以同時支撐200 個客戶端進行并發(fā)訪問,現(xiàn)在發(fā)現(xiàn)流量高峰時有600 個客戶端并發(fā)訪問,則需要3 臺服務器對外提供服務。集群部署架構圖如圖1 所示。
圖1 單體架構系統(tǒng)的集群部署
1.3.1 session 同步問題
當客戶端對Nginx 服務器進行訪問時,Nginx服務器會將請求轉發(fā)至某一臺服務器,可以采用輪詢、同一客戶端IP 轉發(fā)至同一服務器、權重設置等方式實現(xiàn)負載均衡,從而將請求流量分攤到不同服務器,達到支持大并發(fā)量的目的。根據(jù)session 生成的基本原則,可以看出:當用戶訪問服務時,屬于某一用戶的session 只能保存在某一臺服務器上;當用戶下次訪問時,就可能切換到其他單體系統(tǒng)的服務器,當前服務器則沒有屬于該用戶的session 對象,因為屬于這個用戶的session 保存在上一個服務器里,把這種問題可以稱之為“session 不同步”。雖然可以通過“同一客戶端IP 轉發(fā)至同一服務器”原則來保證原用戶導向原服務器,但是一旦這個服務器掛起,則又會出現(xiàn)上述“session 不同步”的問題。
1.3.2 session 存儲問題
當用戶量上升時,session 對象依然存儲在服務器上,對服務器的負擔依然沒有降低,如果單臺服務器掛起時,session 也隨之消失。
第一類終端表現(xiàn)為傳統(tǒng)APP。從Android Native APP、iOS APP 到考慮各類平臺快速部署和兼容問題提出的Web APP,再到兼顧Native APP和Web APP 優(yōu)點的混合終端都以各自的方式接入到服務器系統(tǒng)。
第二類終端表現(xiàn)為依托第三方認證的APP。近幾年,隨著微信和支付寶的快速發(fā)展,一類依托微信建立微信公眾號、小程序和支付寶小程序也廣受企業(yè)用戶歡迎,他們既要與當前系統(tǒng)服務器通信,還要調取微信和支付寶授權信息。
第三類終端表現(xiàn)為服務器API 消費者。如提供數(shù)據(jù)作為一種基礎應用提供給其他第三方消費,此類供調取方消費的API 需求也越來越多。大數(shù)據(jù)時代到來各類數(shù)據(jù)中心積累的數(shù)據(jù)越來越多,應充分利用這些數(shù)據(jù)并將數(shù)據(jù)安全保障的對社會公開。
以Spring Cloud 技術體系為例,通常這一架構構成主要有:注冊中心集群、配置中心集群、網(wǎng)關集群、業(yè)務系統(tǒng)1 集群、業(yè)務系統(tǒng)2 集群、業(yè)務系統(tǒng)3集群……。具體如下圖2 所示。
圖2 微服務通用架構
注冊中心:目前主流的注冊中心組件有zookeeper、eureka、nacos 等,有了注冊中心之后,所有的服務都需要到注冊中心進行登記,所有的服務調用都需要通過注冊中心發(fā)現(xiàn),進行統(tǒng)一管理,即使服務部署在不同的機器上,注冊中心也可以進行統(tǒng)一管理,且可以很好的實現(xiàn)負載均衡。
配置中心:配置是指服務的統(tǒng)一約定、環(huán)境參數(shù)等信息,建立配置中心的目的是讓這些配置可以統(tǒng)一管理、動態(tài)刷新、實現(xiàn)不同環(huán)境下配置切換,每一個服務都可以將自己的配置信息放置到配置中心。配置中心組件有:config-server、nacos 等。
網(wǎng)關:所有的請求都必須通過網(wǎng)關才可以進入,網(wǎng)關相當于客戶端訪問服務的路由器[2],對于客戶端來說屏蔽了服務提供者的內部地址,還可以通過網(wǎng)關的過濾器實現(xiàn)請求的攔截、實現(xiàn)響應的前置處理和后置處理等[3]。主流的網(wǎng)關組件有:zuul、Spring Cloud Gateway 等。
業(yè)務系統(tǒng):一般業(yè)務系統(tǒng)會將用戶中心獨立出來變成一個基礎子系統(tǒng)獨立部署,然后再將其他業(yè)務系統(tǒng)按照微服務劃分服務的原則,獨立出若干個子系統(tǒng),每個業(yè)務子系統(tǒng)都會注冊到注冊中心,都會通過配置中心拉取配置信息,同時要想訪問業(yè)務子系統(tǒng)都會從網(wǎng)關接入。
2.3.1 跨域
整個微服務架構中包含了很多業(yè)務服務器,服務器之間不可能采用相同域名或IP,這就要求對外提供服務時,需要支持跨域,而跨域之后cookie 是不能進行共享的,也就是服務A 并不能拿到服務B的cookie 信息,這也是Web 安全規(guī)則中要求的。
2.3.2 服務無狀態(tài)
多個服務之間相互通信,由于不能共享cookie,就無法記住某一次請求在不同服務之間是否為同一個用戶,這些服務無法保留請求的狀態(tài)信息,或者即使保留了服務的狀態(tài)也是沒有實際意義的,因為這些信息不能代表是同一個用戶。
2.3.3 終端不支持cookie
Native App 以及第三方API 請求的終端并不能支持cookie 機制,也就是說這些架構設計開發(fā)的應用不能記住服務器回寫的終端數(shù)據(jù),因此不能記住用戶身份,即使通過OkHttp 等框架可以實現(xiàn)記住cookie 信息,也會對客戶端架構設計增加很多開發(fā)成本,如果開發(fā)者沒有遵循開發(fā)原則,也會導致無法識別用戶身份信息。
Shiro 是權限管理中非常出色的框架之一,它提供了認證、授權、加密、會話管理、Web 集成、緩存等功 能 。 Shiro 主 要 包 括 Subject、SecurityManager、Realm 等主要組件,開發(fā)通過實現(xiàn)自定義的Realm就可以實現(xiàn)授權和鑒權。
shiro 的鑒權是通過為需要限制訪問的資源方法 添 加 @RequiresUser、@RequiresRoles、@Requires Permission 等注解來實現(xiàn)的[4]。這些注解本質上是通過AOP 來實現(xiàn)的,通過給這些注解傳遞參數(shù),使用@RequiresUser 表示只允許指定的用戶訪問,使用@RequiresRoles 表示只允許指定的角色訪問,使用@RequiresPermission 只允許擁有某權限的用戶訪問。當請求需要訪問這些受限資源方法時,AOP 就會來判斷這些限制訪問的資源是否允許訪問。
JWT 是一種基于JSON 的開放標準,定義了一套在不同實體之間傳遞安全信息的數(shù)據(jù)格式標準,最終由客戶端向服務端發(fā)送一個token(令牌)表示客戶端用戶的身份信息,整個信息傳輸又是建立在加密算法的基礎上,這樣既保證token 的安全,又能讓服務器識別出用戶身份。這個token 信息包含3個部分:head 部分(聲明token 類型和加密算法)、payload 部分(數(shù)據(jù)主體:用戶、過期時間等)、signature 部分(驗證數(shù)據(jù)是否被篡改的簽名)。它的優(yōu)點在于不需要依賴cookie 和session,JWT 非常適合微服務架構和不支持cookie 的各類終端。
3.3.1 單體下使用Shiro 和JWT
首先,需要實現(xiàn)HostAuthenticationToken 接口定義JwtToken 類,用于按照JWT 數(shù)據(jù)標準標識用戶身份信息。
其次,定義類JwtFilter 繼承BasicHttpAuthenti cationFilter 類,對每一個攜帶jwtToken 的請求進行認證和授權,只有通過認證的才可以授權放行。
最后,定義JwtRealm 類繼承AuthorizingRealm,實現(xiàn)認證和授權核心機制。具體認證和授權流程如圖3 所示。
圖3 單體架構下使用shiro 和jwt 實現(xiàn)認證和授權
步驟1-4:客戶端用戶輸入賬號和密碼發(fā)起登錄請求,服務器使用Shiro 框架對賬號和密碼進行驗證,驗證通過發(fā)放JwtToken。
步驟5:用戶攜帶合法JwtToken 訪問受限資源,經過JwtFilter 過濾器,JwtFilter 對請求進行攔截。
步驟 6:JwtFilter 對 JwtToken 進行驗證,將JwtToken 傳遞給 Shiro 和 JWT 組件。
步驟 7:Shiro 和 JWT 使用 Realm 對 JwtToken進行驗證,驗證通過返回,并對用戶進行授權。
步驟8:JwtFilter 放行對受限資源的請求,轉發(fā)請求至受限資源。
步驟9:受限資源的權限注解被調用,將使用Shiro 的 isPermitted()方法驗證權限。
步驟10:Shiro 驗證權限通過。
步驟11:返回受限資源到用戶端。
3.3.2 多服務架構改造
基于單體架構認證和授權的基本原理,改造成適合微服務架構的認證和授權機制。首先,創(chuàng)建類JwtGateWayFilter 實現(xiàn) GatewayFilter 接口,注意與之前單體架構實現(xiàn)的接口不同,GatewayFilter 接口是來自于微服務架構中的網(wǎng)關組件,目的是對用戶發(fā)起的請求中JwtToken 信息進行驗證;其次,繼續(xù)保留JwtToken 類的定義,用于認證和授權過程中token 的傳遞;最后,為每一個微服務創(chuàng)建JwtFilter 類繼承BasicHttpAuthenticationFilter 類,創(chuàng)建 JwtRealm 類繼承AuthorizingRealm 類,實現(xiàn)授權機制。
與單體架構不同之處:
(1)全局過濾器添加在微服務的網(wǎng)關上,用于判斷用戶是否獲得合法認證做驗證。
(2)每一個微服務需要進行授權操作。
(3)每一個微服務獨立實現(xiàn)鑒權機制。
整個流程圖如圖4 所示。
圖4 微服務架構下使用shiro 和jwt 實現(xiàn)認證和授權
步驟1-4:登錄通過認證,獲得token(JWT)。
步驟5:用戶訪問業(yè)務資源;
步驟6:網(wǎng)關使用JwtGatewayFilter 對token 進行驗證,如果驗證通過放行請求。
步驟7:業(yè)務集群使用Shiro 和JWT 對token 進行驗證并授權,放行請求至受限資源,返回受限資源信息。
步驟8:返回受限資源消息至用戶。
微服務架構下的業(yè)務系統(tǒng)各自相對獨立,每一個業(yè)務系統(tǒng)都獨立部署,各個服務之間是處在一種跨域的環(huán)境中,加上客戶端技術架構多樣化,這就要求對傳統(tǒng)Web 認證和授權機制進行重構。既要支持業(yè)務集群的分布式環(huán)境,又要簡化認證授權流程,同時還需要減少服務器開銷,最終還需要保證Web 服務的安全性和可靠性。