首頁 > 科技

阿里大牛整理!SpringBoot動態許可權變更實現的整體方案(附源碼)

2021-06-29 16:36:27

1、前言

在Web項目中,許可權管理及許可權訪問控制為網站訪問安全提供了保障,並且很多項目使用了Session作為快取,結合AOP技術進行token認證和許可權控制。許可權控制流程大致如下圖所示:

現在,如果管理員修改了使用者的角色,或修改了角色的許可權,都會導致使用者許可權發生變化,此時如何實現動態許可權變更,使得前端能夠更新使用者的許可權樹,後端訪問鑑權AOP模組能夠知悉這種變更呢?

2、問題及解決方案

現在的問題是,管理員沒法訪問使用者Session,因此沒法將變更通知此使用者。而使用者如果已經登入,或直接關閉瀏覽器頁面而不是登出操作,Session沒有過期前,使用者訪問介面時,訪問鑑權AOP模組仍然是根據之前快取的Session資訊進行處理,沒法做到動態許可權變更。

使用Security+WebSocket是一個方案,但沒法處理不線上使用者。

解決方案的核心思想是利用ServletContext物件的共享特性,來實現使用者許可權變更的資訊傳遞。然後在AOP類中查詢使用者是否有變更通知記錄需要處理,如果許可權發生變化,則修改response訊息體,新增附加通知資訊給前端。前端收到附加的通知資訊,可更新功能許可權樹,並進行相關處理。

這樣,利用的變更通知服務,不僅後端的使用者url訪問介面可第一時間獲悉變更,還可以通知到前端,從而實現了動態許可權變更。

3、方案實現

3.1、開發變更通知類

服務介面類ChangeNotifyService,程式碼如下:

服務實現類ChangeNotifyServiceImpl,程式碼如下:

此外,變更通知類型,與使用的demo項目有關,目前定義了4種變更通知類型。實際上,除了許可權相關的變更,還有與Session快取欄位相關的變更,也需要通知,否則使用者還是在使用舊資料。

3.2、將變更通知類物件,納入全局配置服務物件中進行管理

全局配置服務類GlobalConfigService,負責管理全局的配置服務物件,服務介面類程式碼如下:

服務實現類GlobalConfigServiceImpl,程式碼如下:

GlobalConfigServiceImpl類,管理了很多配置服務類,此處主要關注ChangeNotifyService類物件。

3.3、使用ServletContext,管理全局配置服務類物件

全局配置服務類在應用啟動時載入到Spring容器中,這樣可實現共享,減少對資料庫的訪問壓力。

實現一個ApplicationListener類,程式碼如下:

在啟動類中,加入該應用偵聽器ApplicationStartup。

現在,有了一個GlobalConfigService類型的全局變數globalConfigService。

3.4、發出變更通知

此處舉2個例子,說明發出變更通知的例子,這兩個例子,都在使用者管理模組,UserManServiceImpl類中。

1)管理員修改使用者資訊,可能導致許可權相關項發生變動,2)禁用使用者,發出變更過通知。

發出通知的相關程式碼如下:

本demo項目的角色相對較少,沒有使用使用者角色關係表,而是使用了bitmap編碼,角色ID取值為2^n,使用者角色組合roles欄位為一個Integer值。如roles=7,表示角色ID組合=[1,2,4]。

另外,如果修改了角色的功能許可權集合,則需要查詢受影響的使用者ID列表,依次發出通知,可類似處理。

3.5、修改Response響應訊息體

Response響應訊息體,為BaseResponse,程式碼如下:

BaseResponse類增加了Additional類型的additional屬性欄位,用於輸出附加資訊。

Additional類的定義如下:

附加資訊類Additional中,個屬性欄位的說明:

notifycode,為通知碼,即可對應通知訊息的類型,目前只有一種,可擴展。notification,為通知碼對應的訊息。通知碼,在ExceptionCodes列舉檔案中定義:

token,用於要求前端更新token。更新token的目的是確認前端已經收到許可權變更通知。因為下次url請求將使用新的token,如果前端未收到或未處理,仍然用舊的token訪問,就要跳到登入頁了。rights,功能樹的字元串輸出,是樹型結構的JSON字元串。3.6、AOP鑑權處理

AuthorizationAspect為鑑權認證的切面類,程式碼如下:

AuthorizationAspect類定義了切點verify(),@Before增強用於鑑權驗證,增加了對變更通知資訊的處理。並利用Session,用rightsChanged屬性欄位記錄需要通知前端的標誌,在@AfterReturning後置增強中根據該屬性欄位的值,進行一步的處理。

@Before增強的doVerify方法中,如果發現角色組合有改變,但仍有訪問此url許可權時,會繼續後續處理,這樣不會中斷業務;如果沒有訪問此url許可權,則返回訪問受限異常資訊,由前端顯示訪問受限頁碼(類似403 Forbidden 頁碼)。

在後置增強@AfterReturning中,限定了返回值類型,如果該請求響應的類型是BaseResponse類型,則修改reponse訊息體,附加通知資訊;如果不是,則不處理,會等待下一個url請求,直到返回類型是BaseResponse類型。也可以採用自定義response的header的方式,這樣,就無需等待了。

generateToken方法,是LoginService類的靜態方法,用於生成使用者token。

至於Utility的parseRoles方法,是將bitmap編碼的roles解析為角色ID的列表,程式碼如下:

getRoleRights方法,是角色功能許可權服務類RoleFuncRightsService的方法,它提供了根據List類型的角色ID列表,快速獲取許可權許可權樹的功能。

如何獲取?

轉發分享此文,後臺私信小編:「 資料 」即可獲取。(注:轉發分享,感謝大家)


IT145.com E-mail:sddin#qq.com