1. 程式人生 > >通用型認證與授權最小系統,以較小的代價實現中立開源專案:UniAuth

通用型認證與授權最小系統,以較小的代價實現中立開源專案:UniAuth

一個 App + 一個 DB時代的終結

五年前,就如很多創業公司剛開始的時候一樣,點融網的主要業務架構在一個被稱為MainApp + Workflow上的應用:MainApp處理投資者的投資、充值、提現等投資端的操作;用Workflow來處理所有的進件、審批、放款、催收等貸款端的操作。當業務體量並不大的時候,世界一切都顯得那麼簡單。前面一個包含了MainApp + Workflow的應用,後面一個包羅永珍的資料庫。一切為了快速的迭代和發展!彼時那是一個 App + 一個 DB的時代。

斗轉星移,點融網漸漸從一個小荷才露尖尖角的小松鼠,發展成一個互金領域的強力領軍人物之一。發展到這個階段的公司,很多都會經歷業務的複雜度極具上升,需要通過分拆業務系統來承載更多的業務流量及複雜性。於是很自然的,前邊的應用,從1變成了N。點融的世界開始變得不是那麼簡單。我們的工程師面對著 N * App 的場景。每個應用都在做自己的許可權控制模組。因為,似乎每個應用的服務物件都不盡相同。有銷售、有運營、有技術支援、有財務審計等等。

所以,誰/何種角色,對於某些特徵資源擁有怎樣的訪問許可權?這是縈繞在很多點融工程師心中的一個問題。

新的突破:認證和授權功能- UniAuth

兩年前,幾位點融的工程師,被賦予了這樣的使命。以較小的代價實現中立的,脫離於特定業務場景的 認證(authentication) 和 授權(authorization) 功能 – UniAuth。

擺在這些工程師面前的有這樣一些問題:市場上是否有類似的系統可以被使用呢?

UniAuth這種型別的系統,在市場上叫做 IAM (Identity Access Management) 。UniAuth既是以做輕量化的IAM為目標。 這方面在美帝做得最好的公司叫做 okta(15年估值15億美金) ,  amazon 的AWS裡面也有IAM。 美國的公司, 對於國內有牆, 伺服器不穩定的可能性、對中國support不好 這些因素導致我們很難選擇美國的IAM產品, 並且這些服務,本身的資費對於一個處於成長期的公司而言太貴,同時對於互金類公司而言,對於資料/系統的安全性的考量,我們會更傾向於基於框架上的二次開發模式。 縱觀當時市場上輕量級的 IAM 系統, 要麼沒有開源滿足點融需求可用的、要麼僅存在於精美的PPT當中。

基於成本的考量,當時的點融還沒有相應的預算投入到這些在業務價值中的優先順序較小的專案中。

基於定製化需求的考量 自己公司做一個,持續投入資源 在support上形成優勢。UniAuth的目標

  • 相容目前子系統的許可權控制模型子系統的許可權模型可以適配轉換進入新系統的許可權模型。
  • 具有廣泛的第三方鑑權系統或協議的可接納性需要廣泛的鑑權系統/協議的整合支援,包括但不限於:sso,oauth,ntlm/kerberos,openid,ldap/ms active directory,saml…因為說不清未來要整合什麼東西。但有需要時可以通過配置,或以較小的代價接入。
  • 具有良好擴充套件性擴充套件性表現在認證和授權的各個階段和環節,每個環節都有預設實現,但可以提供途徑根據需要進行覆寫和干預。這通常發生在子系統認為框架提供的某個環節不爽的場景,比如我覺得公共登入頁很醜,我要定製自己的登入頁。
  • 方便的組,角色,人員許可權分配和控制當新加入業務操作人員時,管理人員只需要簡短操作就可以方便的進行賬戶許可權分配,控制,管理,許可權開關設定等,
  • 程式碼侵入性許可權控制對系統業務級程式碼無侵入性,或有較少侵入性。
  • 使用成熟解決方案,不重複造輪子使用業界久經考驗的成熟開源方案,少些程式碼,遇到問題通過社群很快得到解決。

基於以上設計原則,我們選擇了CAS + Spring Security的開源組合來作為我們Uniauth框架開發的基礎、設計一套基於mysql的許可權模型 將其融入到SpringSecurity中,為點融網的前後端分離進行了定製化,Uniauth雛形就有了。

架構設計

 系統依賴架構圖:

 系統依賴架構

在這樣的架構下, cas伺服器獨立地存在, 擁有了擴充套件uniauth-server 資料來源的可能性,可以同時 從LDAP取得 企業使用者資料, 也允許客戶端以純webservice的形式整合到Uniauth當中。 在authentication、authorization、data level filter的整個鏈條中,每一個鏈條都可以被打斷。

舉例來講,我們來了一個python base的客戶,他只想要做authentication, 那麼在上圖中, 只需要通過cas完成authentication拿到使用者的identity, 然後再使用identity去uniauth-server 得到該identity的profile即可滿足其需求,整個流程全部以webservice的形式完成, 與客戶端語言無關。

再舉例來講,資料系統想要資料,直接問uniauth-server的api索取即可。當然預設情況下我們提供了強大的基於SpringSecurity的CAS客戶端,內網Java base的客戶整合起來要相對容易很多。

專案內部的模組依賴圖:

專案內部

原始碼模組化, 比如客戶端(uniauth-server的) 如 業務系統、資料系統 僅依賴common模組即可訪問uniauth-server的讀介面,並且儘可能少的依賴jar包。客戶端(uniauth-server的) 如 techops、cas 僅依賴share-rw即可訪問 uniauth-server的讀 和 寫 介面。同時 uniauth-server提供的介面是基於jax-rs標準的json 介面, 所以異構系統完全可以自己寫客戶端訪問uniauth-server.

整合系統 整合Uniauth框架圖:

整合Uniauth

該圖描述了一個以正常形式整合Uniauth系統的java base的整合方的流程。

SSO純API整合流程圖:

SSO純API整合

Uniauth提供了純API的形式整合Uniauth的系統, 該方式給予了客戶最大化的自由,不想使用我們提供的任何jar包,或者異構系統。整合想要自由,就給它自由,  而且自由的同時保證了認證機制的安全。

資料庫模型圖(在沒有加上SaaS化之前的版本):

資料庫模型

現在的資料庫裡面對於User表又擴充套件了其EAV模型, 增加了SaaS etc..幾個比較重要的點:

  1. 所有的實體資料都不會被刪除,只會被禁用(status欄位)
  2. 角色可以通過組賦予與整合、也可以直接賦予人
  3. 組樹狀結構的closure table設計
  4. 不同的domain擁有不同的role 和 permission, 許可權資料在不同整合系統之間隔離,而user和group資料又是共享的
  5. audit表通過aop, 記錄下對資料庫 和 api的一切訪問軌跡.
  6. 樹狀資料庫設計

為了避免過長的篇幅描述UniAuth中多個樹狀資料結構,以一個網易評論樹來做講解

資料結構

UniAuth採取了閉包表的資料設計方式:

資料設計

Comment Table Data:

Comment Path Table Data:

這種設計, comment table本身並不儲存 評論與評論之間的關係, 而將該關係用另外一張表(comment_path)儲存起來, 理論上講需要O(n²)的空間來儲存關係,但現實中並不會需要這麼多。

 資料結構關係:

每根紅線都是comment_path中的一條資料, 線條上的數字為depth。

查詢直接回復4號comment的comment(父查子)

select c.* from comment c join comment_path cp on (c.id = cp.descendant) where cp.ancestor = 4 and depth = 1;

查詢所有回覆4號的子comment(父查所有子)

select c.* from comment c join comment_path cp on (c.id = cp.descendant) where cp.ancestor = 4;

–如果你需要保留層級關係, 則將cp中的值也返回即可

查詢所有7號的 父comment(子查所有父)

select c.* from comment c JOIN comment_path cp on (c.id = cp.ancestor) where cp.descendant = 7;

新增一條子回覆到 6號comment上(新增)

step a:

insert into comment(value, topic_id, user_id) values(‘(10)我以gin食阼啦’, 1, 2);

— 拿到該句返回的id, 假設為10

step b:

insert into comment_path (ancestor, descendant, depth) select cp.ancestor, 10, cp.depth+1 from comment_path as cp where cp.descendant=6 union all select 10, 10, 0;

— 只要擁有子comment_id為6作為子節點的節點,全都新增一個id為10,depth+1的子節點 並且插入一個10, 10, 0的節點.

從評論鏈中刪除4號comment及其子comment(刪除子或者子樹)

delete a from comment_path a join comment_path b on (a.descendant = b.descendant) where b.ancestor=4;

— 這句話等價於 “delete from comment_path where descendant in (select descendant from comment_path where ancestor = 4);”, 但mysql會報from句子中的表不能用於update

將6號comment的父comment更改為2號(移動子或者子樹)

step a:

delete a from comment_path as a join comment_path as d on a.descendant = d.descendant left join comment_path as x on x.ancestor = d.ancestor and x.descendant = a.ancestor where d.ancestor = 6 and x.ancestor is null;–這樣刪除的原因和需求5一致.

step b:

insert into comment_path (ancestor, descendant, depth) select supertree.ancestor, subtree.descendant, supertree.depth+subtree.depth+1 from comment_path as supertree join comment_path as subtree where subtree.ancestor = 6 and supertree.descendant = 2;

以上6種需求覆蓋了最為常用的幾種情況,解決了基本上UniAuth在閉包表上遇到的所有的問題。

closure table是反模式設計的一種經典設計, 在結構化資料庫裡面,SQL可以很輕易高效地 支援對樹的各種各樣的增、刪、改、查、移的需求。這種設計給到UniAuth系統中的資料表設計特點給了很大的支援。

UniAuth優勢分析

成熟度:

市場上會有一些開源的IAM系統。相較於很多專案在專案前期處於Bug較多的探索時期,UniAuth經過兩年多的點融內部研發,和互金生產環境的檢驗,UniAuth已經是一個成熟的生產環境質量的產品。

 成本:

相較於市場上一些IAM系統不菲的軟體授權費用/授權使用費用,UniAuth將專案原始碼完全開源。並鼓勵更多的極客可以貢獻的程式碼,讓很多公共模組可以投入更少,成效更快。

 框架支援與拓展:

UniAuth的底層架構實現了SpringSecurity, 並且通過CAS實現Authentication, 因此可以支援包括SPEL在內的所有CAS, SpringSecurity的特性及其拓展。

資料庫及設計:

對於早期的創業公司而言,成本控制永遠是一箇中心話題。基於MySQL的資料庫,降低了很大的運營成本。並且,UniAuth的資料庫設計,對於樹狀結構資料的增改,做了大量優化和特定設計。這會在後文提到。

客戶端支援/跨域訪問:

同時支援客戶端和REST API訪問。解決跨域訪問問題。

SSO:

CAS天生自帶SSO的實現, 為應用的Authentication提供更多的擴充套件可能性。

UniAuth 開源

作者介紹

錢晟龍,UniAuth專案組成員,點融網資深軟體開發工程師,嘗試設計並解決各類軟體開發中遇到的實際問題,喜歡從已有成熟解決方案中找尋輪子,前端、後端、運維、溝通、管理、寫運營稿,哪裡重要去哪裡, 現主要負責點融網SSO、OAuth2、JWT etc .. 認證授權相關功能。

文章來自微信公眾號:高效開發運維