閱讀重點
Inkmagine 以最小化系統設計提升彈性,開發 API 中介系統 (Gateway) 分散 SSO 系統負載,強化登入穩定性。本次分享聚焦登入流程架構演進及優化,包含 OAuth 流程、C4 Model、系統互動、Client、Authorization Server (Member)、Resource Server (Gateway) 開發項目、程式架構 (UML)、MongoDB 調整注意事項與會員資料同步優化及索引調整策略。
撰文:Satoshi
個人部落格: Medium
Inkmagine 秉持「最小化系統」設計理念,旨在提升系統彈性,避免因共用資料結構而限制功能開發。為實現多系統間流暢登入體驗,我們於 Gateway (API 中介系統) 開發前導入Member (SSO 會員系統)。然而,隨著使用者規模增長,SSO 系統需承擔更高的流量壓力,為確保登入服務的穩定性,我們延續最小化系統概念,開發中介系統作為登入及資料存取的統一窗口。此舉能有效分散 SSO 系統的負載,提升整體系統的安全與穩定性。
本次分享內容將聚焦於登入流程架構的演進與相關優化:
登入流程架構
OAuth 流程簡述

Client --> Authorization Server: 轉導向至Member(Authorization Server)
Authorization Server: 對Client帶入的資料驗證,包含member, OAuth client
Authorization Server --> Resource Server: Authorization Server向Resource Server取得OAuth client及auth code
Authorization Server --> Client: auth code帶入並轉發回Client
Client --> Resource Server: Client拿著auth code向Gateway(resource server)取得token
Resource Server --> Client: Gateway回傳token給client
Client --> Resource Server: Client帶著token向Gateway取得相關資源
需求及開發項目
- 媒體網站端(Client)
- 需求:調整會員登入驗證,由 Member 改串接 Gateway。
- 開發項目:
- OAuth client ID 由 Member OAuth client ID 改為 Gateway OAuth client ID。
- 移除提供給 Member 的 OAuth hook,改串接 Gateway 提供的 hook 規格 (https://gateway.inkmaginecms.com/docs/#/webhook/member)。
- Member
- 需求:接收媒體網站端或用戶端登入請求;若 Client 提供的 OAuth client ID 為 Gateway 的,則將 Client 傳入的 OAuth client 參數轉發給 Gateway 進行驗證;驗證通過後,核發 auth code 給 Member,並將 Gateway 核發的 auth code 轉發回 Client。
- 開發項目:
- 串接 Gateway 的 get OAuth Client API。
- 串接 Gateway 的 issue auth code API。
Gateway
- 需求:驗證 Member 傳入的 OAuth client ID,核發 auth code 給 Member;Client 持 auth code 向 Gateway 請求 access token,並使用該 token 向 Member 取得會員資料。
- 開發項目:
- get OAuth Client API。
- issue auth code API。
- issue access token API。
- get current Member API。
程式架構

- 說明:
OAuthService
: 負責OAuth Client、OAuth Auth Code、OAuth Scopes等資源調度及業務邏輯處理。MemberService
: 負責 Member、Services、Certificate 等資源調度及業務邏輯處理。OauthAuthCodeRepository
: 管理 OAuth Auth Code 資料存取。OauthClientRepository
: 管理 OAuth Client 資料存取。OauthScopeRepository
: 管理 OAuth Scope 資料存取。UserRepository
: 管理 Member 資料存取。UserStatusRepository
: 管理 Member Status 資料存取。ApiFormatTrait
: 負責將資料轉換為 API 所需格式,透過MemberProxy
進行轉換。MemberHook
: 處理接收到的 hook 請求,進行對應的資料存取。
- 流程:Controller 透過各 Service 向 Repository 取得資料,Service 進行業務邏輯整合後回傳給 Controller。Controller 再利用
ApiFormatTrait
轉換資料格式,並透過apiMessageFormat
整理 API Response 格式 (Controller 流程邏輯置於apiErrorHandling
中以統一處理異常)。
轉 MongoDB 的調整
- 資料庫選型考量:因專案特性,需高吞吐量、大資料量儲存及一定程度的資料一致性,故選用 MongoDB,並依官方建議安裝
mongodb/laravel-mongodb
套件。 - 開發注意事項:
- Migration 檔案中的
BluePrint
類別需替換為MongoDB\Laravel\Schema\Blueprint
。 - 一般 ORM Model 繼承的
Model
需替換為MongoDB\Laravel\Eloquent\Model
;會員 ORM Model 繼承的Authenticate Model
需替換為MongoDB\Laravel\Auth\User as Authenticatable
。 - Index 使用 B tree 結構,建立複合索引時需遵循最左原則。
- 建立複合式index時,建議遵循ESR rule
- 對於包含 "or" 條件且需排序的查詢,建議將每個 "or" 的 key 與排序的 key 綁定為獨立索引,以利 optimizer 進行 merge index query。
- 不支援 nested transaction。
- 使用 transaction 時需確保在 60 秒內完成。
- Migration 檔案中的
issue authorization code API 優化
- 當 Gateway 資料庫中不存在會員資料時,向 Member 的 JWT API 發送請求以取得會員資料,並觸發
MemberHook
job 進行資料新增。

- 當 Gateway 資料庫中不存在會員 console 資料時,向 Member 的 JWT API 發送請求以取得會員資料,並觸發
MemberHook
job 進行資料新增。

- 為
MemberHook
加入第三個參數,允許呼叫方控制是否向 Member 的 JWT API 發送請求取得會員資料。

背景說明:測試期間發現登入速度變慢,經追查發現,當檢查會員是否存在於 Gateway 資料庫時,若不存在,會再次向 Member 的 JWT API 驗證,但因為呼叫 MemberHook
時,也會再去對 Member 的 JWT API 發送請求取得會員相關資料。為避免重複請求,在呼叫 MemberHook
時增加參數進行控制。
會員 collection index 調整
- 問題描述:原始會員資料在
status key
下的儲存結構為{"status":{"_id":{"1":true}}}
。原預期針對"status"
建立索引即可在以特定 console (_id
為例) 的狀態進行查詢時命中索引。 - 發現:要針對
_id
為 1 的狀態條件下查詢,並要使 query 命中 index 的話,需要將"_id"
底下的"1"
這個 key 單獨建立 index,例如: db.your_collection.createIndex({"status._id.1":1}),這樣才能使 MongoDB 的Optimizer 使用 index 做條件查詢。 - 考量:Inkmagine 服務系統中 console 可能動態增減,原儲存結構不利於此情境,因為 console 的增刪需要手動維護索引。
- 解決方案:將
status
資訊獨立拆分為另一個 collection 儲存,並在該 collection 下建立索引,以提升查詢效率及維護彈性。
以上分享,若有任何回饋,歡迎聯繫我們
想了解更多?請洽官網 https://www.inkmaginecms.com/