基於 Event-Driven Architecture 為微服務架構打造 Auth Service

PeerOne CTO
PeerOne Technology 皮偶玩互動科技
7 min readMar 9, 2021

隨著容器化技術與管理服務越來越成熟,微服務架構(Microservice Architecture)已經成為多數主流的架構骨幹,相較於傳統的單體式架構(Monolithic Architecture),微服務為大型網站、軟體帶來了應變速度快、彈性高等等的優點,然而同時也大幅地提升了系統的複雜性。

基於單體式架構而設計的驗證系統(authentication)相對單純,只要在 auth layer 處理並驗證 token;而微服務中架構中,用戶可能同時與多個 service 進行溝通,因此,傳統的單體架構的驗證模式並不完全適用於微服務架構。本文將探討不同的方案,並基於 Event-Driven Architecture 設計一套適合於微服務架構的 auth service。

單體式架構的 Auth 設計

在探討微服務架構之前,先回顧一下單體式架構做 auth 的方法

Authorization mechanism of monolithic architecture

在 client 端送出 request 至後端時,會先經過一層 auth layer ,從 cookie、session 或是 token 對用戶進行身份驗證。

微服務架構之 Auth Service

由於 client 端可以直接與各個微服務進行溝通,單體式架構的 auth 機制不再適用,以下提出兩種策略:

Strategy #1 - 將 Auth Service 獨立成一個微服務

建立一個專門做驗證的 auth service,在 client 的 request 進入所有microservices 之前,先進入 Auth Service 進行驗證。

Authorization strategy #1 of microservice architecture

優點:除了 Auth Service,其他微服務不需嵌入 auth 的邏輯,且用戶 auth 狀態修正時,可立即反應至所有服務上。

缺點:當 Auth Service 故障時,須經過 Auth Service 的應用皆無法使用。

Strategy #2 - 將 Auth Logic 存放於各個微服務

Authorization strategy #2 of microservice architecture

優點:當其中一個微服務的 Auth Service 故障時,不會影響其他 Service。

缺點:
1. 所有 services 皆需存放 auth logic(可藉由打包成 package 解決)
2. 當用戶 auth 狀態更改時(如登出、封鎖等),無法及時反映於所有微服務。

Event-Driven Microservice Architecture for Authentication

以下將基於 Strategy #2,設計一套包含註冊、登入、登出的機制,並利用 event-driven microservice architecture (本文使用 Pub/Sub 等)來解決 auth 狀態變更之情形。

Prerequisites

  1. 了解 Pub/Sub 機制或是 Message Queue 如 RabbitMQ、Kafka。
    註:建議可以使用 google 提供的 Cloud Pub/Sub,提供每個月10GB的免費流量,大大簡化了自己部署與維護的成本,Cloud Pub/Sub 有說不完的優點可以直接看官網介紹。
  2. 了解 JSON Web Token (JWT) 原理
Architecture overview

此架構由下列兩個核心的微服務組成

  1. User Service 提供註冊、登入並發放 token 、登出等功能。
  2. Pub/Sub Messaging Service 提供異步通知各種 event。

註冊流程

sign-up flow

當用戶輸入帳號密碼進行註冊時,先發起 signup request 至 User Service 建立用戶的基本資訊,User Service 同時 publish “User 建立” 的 event(帶有username等資訊),其餘有 subscribe 的 service 皆會收到event並作處相對應的處理。

登入流程

sign-in flow

用戶發送帳號密碼至 User Service,User Service 核對身份後核發 JWT 返回給 client 端。

Client 端之後與其他微服務間的溝通即可攜帶此 JWT 作為驗證。

為何選用 JWT?

在微服務之間沒有共用 database 或 cache的情況下,比起傳統的 token,JWT 直接將使用者資料存在toke中,因此只要簽章檢驗通過即可辨識用戶,在微服務架構中能夠方便地分享使用者資料。
註:有關 JWT 的原理及其優點可以參考 [3]。

What if… 用戶登出、用戶被禁用?

某些情況需要使已經核發的 JWT 失效,遇到此種情形該如何解決呢?

解法:結合 Refresh Token 機制
在 User Service 多儲存一個 Refresh Token,將 JWT 的 expire time 調短(e.g. 30分鐘),refresh token 的 expire time 可以設長一點(e.g. 1 個月),當 JWT expire 時,可以拿 refresh token 去 User Service 交換新的JWT;而當用戶被登出或禁用時,在 User Service 刪除對應的 Refresh Token,此種解法可以使用戶被禁用時,舊有的 refresh token 無法再交換新的JWT。

But!舊有的 JWT 在失效前仍可以使用!

如果我們的應用能夠容忍該 JWT 頂多使用 30分鐘,那可以忽略這個問題,然而在安全性要求較高的應用,該如何解決呢?

解法:黑名單機制
當用戶在 User Service 進行登出、或是被禁用時,User Service 刪除對應的refresh token外,同時 publish 一個 “JWT expired” 的 event 給其他的微服務,所有接受到 event 的微服務將該失效的 JWT 存入黑名單(e.g. redis),由於 JWT 失效的時間非常快,因此黑名單 cache 的失效時間只要設的跟 JWT 失效到期一樣即可!

除了黑名單機制之外,也有其他方式可以使 JWT 失效,可以參考[4]。

Summary

在實踐微服務架構的過程中無可避免地踩了許多坑,而其中驗證與權限管理更是個令人頭痛的議題,網路上的解法非常多種,似乎尚未形成一個共識。

在尋找解法的過程中,耳聞造福千萬莘莘學子的 Udemy 講師 Stephen Grider 有在其課程中[1] 講解相關的議題,本文也參考了他提出的設計,非常推薦他的課程,無論是對新手或是老手都有相當大的幫助。

本文提出一個簡單的思路與分析供大家參考,也非常歡迎留言討論。

--

--