雷云祥,蔣其海,田 杰,段明秀
(吉首大學(xué)計(jì)算機(jī)科學(xué)與工程學(xué)院,湖南 吉首 416000)
在常見(jiàn)的分布式系統(tǒng)架構(gòu)中,一般在每臺(tái)服務(wù)器上都獨(dú)立部署一套完整的權(quán)限系統(tǒng),這樣的架構(gòu)會(huì)導(dǎo)致系統(tǒng)的維護(hù)成本偏高且效率低下.雖然基于Session的單點(diǎn)登錄系統(tǒng)[1]在整個(gè)集群中只有唯一的一臺(tái)權(quán)限控制服務(wù)器,但是通過(guò)身份驗(yàn)證的用戶訪問(wèn)集群中的數(shù)據(jù)資源時(shí),需要先在權(quán)限控制服務(wù)器進(jìn)行鑒權(quán),然后才能訪問(wèn)相應(yīng)的數(shù)據(jù)資源.這樣操作雖然實(shí)現(xiàn)了權(quán)限控制的相對(duì)獨(dú)立,但向集群的所有數(shù)據(jù)請(qǐng)求都要先訪問(wèn)權(quán)限控制服務(wù)器,導(dǎo)致系統(tǒng)響應(yīng)的實(shí)時(shí)性降低,而且當(dāng)權(quán)限控制服務(wù)器死機(jī)時(shí),整個(gè)集群將處于癱瘓狀態(tài).針對(duì)上述認(rèn)證方式暴露的性能和效率低等問(wèn)題,筆者擬在JSON網(wǎng)絡(luò)令牌(JSON Web Token,JWT)技術(shù)的基礎(chǔ)上通過(guò)自定義鑒權(quán)技術(shù),將身份認(rèn)證獨(dú)立為認(rèn)證服務(wù)器以實(shí)現(xiàn)用戶身份的統(tǒng)一認(rèn)證,并將用戶鑒權(quán)分散到各個(gè)業(yè)務(wù)服務(wù)器以實(shí)現(xiàn)對(duì)數(shù)據(jù)請(qǐng)求的鑒權(quán),即采用“集中認(rèn)證,分散鑒權(quán)”模式,以期滿足大部分系統(tǒng)在分布式環(huán)境下對(duì)權(quán)限管理的需要.
JWT是一種用于Web環(huán)境下各方之間傳遞信息的表述性聲明規(guī)范,它定義了一種簡(jiǎn)潔的、自包含的方法,用于通信雙方以JSON對(duì)象的形式安全地傳遞信息.JWT由Header頭部、Payload載荷和Signature簽名3個(gè)部分組成.載荷主要用來(lái)在不同服務(wù)器之間交互信息,它由默認(rèn)預(yù)定義信息和自定義信息組成:默認(rèn)預(yù)定義信息包括簽發(fā)時(shí)間、過(guò)期時(shí)間等;自定義信息是業(yè)務(wù)處理中需要用到的信息,如用戶賬號(hào)信息和權(quán)限信息等.簽名由Header、私鑰和Payload組合加密生成,它實(shí)際上是一個(gè)加密字符串,其中私鑰由創(chuàng)建Token的服務(wù)器提供.簽名的作用是,當(dāng)服務(wù)器接收Token后,通過(guò)拿取Header和Payload信息,再組合服務(wù)器本身的私鑰生成一個(gè)新的簽名,將新的簽名和Token中的簽名進(jìn)行比對(duì),查看是否一致,從而驗(yàn)證Token是否被篡改.
基于JWT技術(shù)的認(rèn)證流程是,通過(guò)身份認(rèn)證的用戶將獲取的Token存儲(chǔ)在客戶端,當(dāng)客戶端每次請(qǐng)求服務(wù)器資源時(shí),在請(qǐng)求頭中攜帶Token,供服務(wù)器實(shí)現(xiàn)鑒權(quán)[2].
Spring Security是一個(gè)安全性框架,能夠與SpringBoot和Mybatis等各大框架集成使用.Spring Security提供了默認(rèn)的十幾種過(guò)濾器,每一種過(guò)濾器都能繼承自定義實(shí)現(xiàn),如“UsernamePasswordAuthenticationFilter”主要用來(lái)實(shí)現(xiàn)登錄認(rèn)證功能.權(quán)限系統(tǒng)關(guān)于安全方面的2個(gè)核心功能是認(rèn)證和鑒權(quán):認(rèn)證是對(duì)用戶進(jìn)行身份驗(yàn)證,判斷用戶是否為系統(tǒng)合法用戶;鑒權(quán)是當(dāng)用戶發(fā)起對(duì)資源的訪問(wèn)時(shí),服務(wù)器判斷用戶是否具備訪問(wèn)請(qǐng)求該資源的權(quán)限[3].
對(duì)于單體架構(gòu)系統(tǒng),認(rèn)證成功后,認(rèn)證信息可以直接存儲(chǔ)到本地服務(wù)器.對(duì)于分布式集群系統(tǒng),由于負(fù)載均衡服務(wù)器將請(qǐng)求分發(fā)到集群中的任何一臺(tái)服務(wù)器上,因此要保證每臺(tái)服務(wù)器鑒權(quán)時(shí)能夠拿到正確的認(rèn)證信息,就不要采用Session認(rèn)證技術(shù),否則會(huì)導(dǎo)致服務(wù)器性能降低.為了解決這個(gè)問(wèn)題,可以將用戶身份信息和權(quán)限信息置于Token中,由客戶端存儲(chǔ)該Token,而服務(wù)器端無(wú)需存儲(chǔ)用戶認(rèn)證信息或會(huì)話信息,以減輕服務(wù)端的壓力.
分布式集群環(huán)境下通過(guò)一臺(tái)鑒權(quán)服務(wù)器進(jìn)行集中鑒權(quán),勢(shì)必會(huì)導(dǎo)致鑒權(quán)服務(wù)器訪問(wèn)壓力大,且當(dāng)訪問(wèn)不需要鑒權(quán)的服務(wù)器時(shí),依然要經(jīng)過(guò)鑒權(quán)服務(wù)器的路由轉(zhuǎn)發(fā),增加服務(wù)響應(yīng)時(shí)間.
動(dòng)態(tài)權(quán)限管理系統(tǒng)[4]采用JWT技術(shù),將認(rèn)證后的賬號(hào)信息存放到Token中,通過(guò)在請(qǐng)求中攜帶Token訪問(wèn)服務(wù)器實(shí)現(xiàn)分散鑒權(quán).但Token中并未存放權(quán)限信息,每次鑒權(quán)需要通過(guò)查詢(xún)數(shù)據(jù)庫(kù)獲取用戶的權(quán)限信息,造成資源浪費(fèi)和性能下降.為了解決這個(gè)問(wèn)題,可以采用“分散鑒權(quán)”模式:一方面,將鑒權(quán)過(guò)程分散到各個(gè)服務(wù)器,當(dāng)認(rèn)證成功后,將權(quán)限信息存儲(chǔ)在Token中,鑒權(quán)時(shí)直接從Token中獲得權(quán)限信息,從而避免頻繁地訪問(wèn)數(shù)據(jù)庫(kù);另一方面,對(duì)于不需要鑒權(quán)的服務(wù)器,就直接去除鑒權(quán)模塊.
JWT簽發(fā)時(shí),在它的載荷部分存放了Token的簽發(fā)時(shí)間和過(guò)期時(shí)間,為了防止Token被盜造成嚴(yán)重的后果,Token的有效時(shí)間一般設(shè)置得很短.這樣會(huì)造成Token頻繁失效,用戶需要不斷重復(fù)登錄,從而大大降低了用戶體驗(yàn).為了解決這個(gè)問(wèn)題,可以通過(guò)引入基于Refresh Token的靜默刷新機(jī)制進(jìn)行Token刷新,在Token失效之前獲取新的Token,解決了用戶頻繁掉線問(wèn)題.
分布式權(quán)限管理系統(tǒng)架構(gòu)如圖1所示.系統(tǒng)通過(guò)Nginx進(jìn)行反向代理,將用戶的請(qǐng)求路由到不同的服務(wù)器,認(rèn)證模塊單獨(dú)拿出,并將用戶認(rèn)證獲取Token放到認(rèn)證服務(wù)器,每一個(gè)Web應(yīng)用服務(wù)器都有自定義的權(quán)限鑒權(quán)模塊.
圖1 分布式權(quán)限管理系統(tǒng)架構(gòu)
用戶進(jìn)行身份認(rèn)證的流程如圖2所示.
圖2 認(rèn)證流程
認(rèn)證具體步驟如下:
(ⅰ)用戶發(fā)送登錄請(qǐng)求到認(rèn)證服務(wù)器.
(ⅱ)認(rèn)證服務(wù)器攔截請(qǐng)求,進(jìn)行用戶身份驗(yàn)證,驗(yàn)證成功則轉(zhuǎn)至(ⅲ),驗(yàn)證失敗則將登錄失敗信息返回給前端.
(ⅲ)查詢(xún)用戶所有資源信息(角色、接口權(quán)限等),按照權(quán)限信息生成算法將擁有的所有應(yīng)用程序編程(Application Programming Interface,API)接口權(quán)限生成為權(quán)限集合,同時(shí)用通用唯一識(shí)別碼(Universally Unique Identifier,UUID)作為Key,將用戶的權(quán)限集合儲(chǔ)存在Redis,用于之后的權(quán)限刷新.
(ⅳ)通過(guò)權(quán)限集合和用戶賬號(hào)等信息生成Access Token,通過(guò)用戶賬號(hào)和權(quán)限集合對(duì)應(yīng)的UUID等信息生成Refresh Token,2個(gè)Token的簽發(fā)時(shí)間和過(guò)期時(shí)間一致.
(ⅴ)將認(rèn)證信息返回給客戶端,客戶端將2個(gè)Token存儲(chǔ)在本地.
服務(wù)器鑒權(quán)功能主要由自定義的過(guò)濾器實(shí)現(xiàn),過(guò)濾器對(duì)客戶端的請(qǐng)求進(jìn)行攔截,流程如圖3所示.
圖3 鑒權(quán)流程
隨著系統(tǒng)業(yè)務(wù)規(guī)模的擴(kuò)大,用戶權(quán)限信息也不斷增加,如果不采用一定的方式進(jìn)行處理,Token的長(zhǎng)度就會(huì)不斷增長(zhǎng),從而出現(xiàn)過(guò)多的網(wǎng)絡(luò)帶寬占用和超出服務(wù)器最大請(qǐng)求長(zhǎng)度等問(wèn)題.
對(duì)于Web應(yīng)用來(lái)說(shuō),權(quán)限信息實(shí)質(zhì)上是用戶所能訪問(wèn)資源的統(tǒng)一資源定位符(Uniform Resource Locator,URL),由于URL滿足層級(jí)結(jié)構(gòu),因此可以將它設(shè)計(jì)成樹(shù)形結(jié)構(gòu).例如“/unit/addUnit”分為“unit”和“addUnit”,其中“unit”結(jié)點(diǎn)為“addUnit”的父結(jié)點(diǎn).
URL之間往往存在包含關(guān)系,如“/user/student/query”包含“user/student”,在樹(shù)形結(jié)構(gòu)下,可以通過(guò)這樣的包含關(guān)系去除重復(fù)的結(jié)點(diǎn),減少權(quán)限信息長(zhǎng)度.但是因?yàn)檫@樣的關(guān)系,在遍歷URL樹(shù)時(shí),難以判斷遍歷的結(jié)點(diǎn)是否為URL最終結(jié)點(diǎn),所以必須給樹(shù)的每個(gè)結(jié)點(diǎn)添加是否為最終結(jié)點(diǎn)的標(biāo)志(圖4).
圖4 權(quán)限樹(shù)
權(quán)限信息生成算法具體流程如下:
(ⅰ)按照“/”分隔,將URL分級(jí)處理成一棵多叉權(quán)限樹(shù),“/”為樹(shù)的根節(jié)點(diǎn).
(ⅱ)將URL權(quán)限樹(shù)序列化成帶固定格式的權(quán)限字符串集合,第2級(jí)添加大括號(hào),第3級(jí)添加中括號(hào),第4級(jí)添加小括號(hào),同級(jí)的URL結(jié)點(diǎn)用符號(hào)“|”分開(kāi).每個(gè)URL后添加0或1,代表是否為最后一個(gè)結(jié)點(diǎn),如{user=0}[register=1|verificationCode=1|applyRetrieveCode=1|retrievePassword=1|]{unit=0}[**=1].
(ⅲ)通過(guò)GZIP壓縮算法進(jìn)行權(quán)限字符串壓縮.
認(rèn)證成功后,在生成Access Token的基礎(chǔ)上也生成Refresh Token,并返回給客戶端.其中Refresh Token包含刷新Token時(shí)需要的用戶賬號(hào)信息和Redis中權(quán)限集合對(duì)應(yīng)的Key.
基于Refresh Token的靜默刷新機(jī)制如圖5所示.
圖5 刷新Token流程
刷新Token具體步驟如下:
(ⅰ)客戶端攜帶Token向服務(wù)器請(qǐng)求資源.
(ⅲ)在超文本傳輸協(xié)議(Hyper Text Transfer Protocol,HTTP)響應(yīng)頭中添加Token狀態(tài)刷新標(biāo)志(refresh_status),同時(shí)服務(wù)器繼續(xù)處理請(qǐng)求,返回?cái)?shù)據(jù)給客戶端.
(ⅳ)客戶端檢測(cè)到refresh_status刷新標(biāo)志,攜帶Refresh Token訪問(wèn)認(rèn)證服務(wù)器的Token刷新接口.
(ⅳ)認(rèn)證服務(wù)器通過(guò)Refresh Token中的UUID去Redis數(shù)據(jù)庫(kù)中查找對(duì)應(yīng)用戶的權(quán)限集合,最后生成新的Token返回給客戶端.
為了測(cè)試新型分布式權(quán)限控制系統(tǒng)與傳統(tǒng)權(quán)限系統(tǒng)的性能差異,在Centos7.9,Tomcat9.0,CPU 2.2 GHz,8.0 GB內(nèi)存的環(huán)境下分別部署這2種權(quán)限系統(tǒng),進(jìn)行實(shí)驗(yàn)對(duì)比.
通過(guò)編寫(xiě)多線程程序模擬用戶訪問(wèn)服務(wù)器(其中一個(gè)線程代表一個(gè)用戶),并在每個(gè)線程中基于HTTP協(xié)議不斷發(fā)送請(qǐng)求來(lái)模擬用戶操作,每次請(qǐng)求計(jì)算從發(fā)送請(qǐng)求到獲取響應(yīng)的時(shí)間間隔,最終統(tǒng)計(jì)出平均響應(yīng)時(shí)間.程序支持開(kāi)啟給定數(shù)量的線程,本次測(cè)試模擬從100遞增到1 000的并發(fā)情況.表1示出了2個(gè)權(quán)限系統(tǒng)在不同用戶數(shù)量情況下平均響應(yīng)時(shí)間的變化情況.
表1 系統(tǒng)平均響應(yīng)時(shí)間對(duì)比
由表1可知,分布式權(quán)限控制系統(tǒng)的平均響應(yīng)時(shí)間始終比傳統(tǒng)權(quán)限系統(tǒng)的短,說(shuō)明新型權(quán)限控制系統(tǒng)在分布式環(huán)境下顯著提升了系統(tǒng)性能.
對(duì)當(dāng)前分布式環(huán)境下權(quán)限系統(tǒng)存在的問(wèn)題進(jìn)行了分析,并在分布式集群環(huán)境下,從認(rèn)證信息存儲(chǔ)、分布式鑒權(quán)和Token刷新機(jī)制等方面設(shè)計(jì)了可行性解決方案.系統(tǒng)采用“集中認(rèn)證,分散鑒權(quán)”模式與傳統(tǒng)認(rèn)證模式相比,能更好地適應(yīng)分布式集群環(huán)境,從而提升系統(tǒng)的整體性能和效率.