下拉重新整理
在 Keycloak 和 HAPI FHIR 中間放一層 Rails(一):它不是 proxy,是 policy gateway

在 Keycloak 和 HAPI FHIR 中間放一層 Rails(一):它不是 proxy,是 policy gateway

682 字 2 分鐘閱讀 16 次閱讀

這系列整理自我跟 AI 一次討論 FHIR 存取架構的對話,把患者端 SMART on FHIR、火線超人(FHIRLineBot)跟 AI 臨床工具要怎麼安全接資料,一次想清楚。

我在規劃患者端的 SMART on FHIR 應用時,卡在同一個問題:細粒度的存取控制到底該放哪裡。病人只能看自己的資料、只能看最近七天、某些欄位要遮蔽、AI Agent 不能亂查別人的病歷,這些規則要塞在哪?

我試過兩個方向。一個是把規則全部硬塞進 HAPI FHIR,用它的 interceptor 去擋。另一個是在 Keycloak 跟 HAPI FHIR 中間,放一層自己的 Rails。試下來我選後者,因為前者很快就會變成一堆散在 Java interceptor 裡、難維護又難稽核的條件判斷。

這一層我定位成 policy gateway,不是 proxy

重點是心態。我不是把 Rails 當成一個「把請求原封不動轉給 HAPI」的透明代理,那樣它只是多一個 hop。我把它定位成 FHIR Policy Gateway,或者說 Clinical Function Gateway,一層真正決定「這次請求在這個情境下,實際能做什麼」的執行層。

整個架構長這樣:

flowchart TB
    A[SMART App / LINE Bot / AI Agent] -->|OAuth Access Token| B
    B[Rails FHIR Policy Gateway] --> C[HAPI FHIR]
    C --> D[Clinical Data Store]
    subgraph B1[Rails 這層做的事]
      direction TB
      T1[驗證 token、scope、patient context]
      T2[套用 constraint]
      T3[執行 consent / purpose policy]
      T4[呼叫 CQL Engine]
      T5[提供受控 function tools]
      T6[稽核與遮罩]
    end
    B --- B1

四個角色,一句話各自定位

我把責任拆成四塊,各講一句就懂:

  • Keycloak:你是誰、拿到什麼授權。負責登入、OAuth 2.0 / OIDC、SMART scope、patient launch context、token 效期。
  • Rails:這次請求在此情境下實際能做什麼。驗 token、正規化 scope、綁 patient、展開 constraint、改寫查詢、過濾回傳、稽核。
  • CQL:臨床條件與決策邏輯。判斷病人符不符合某個臨床條件,不拿來當授權。
  • HAPI FHIR:標準化的臨床資料來源。標準 FHIR REST、驗證、搜尋、版本、history。

為什麼不把這些全塞 HAPI?因為彈性。像「僅限本人、僅限最近七天、禁止 $everything、遮蔽精神科標籤」這種會一直變的業務政策,放在 Rails 我改起來快,也留得下稽核軌跡。HAPI 我留給它最擅長的事:當標準資料來源,順便當最後一道防線。

這一篇先把定位講清楚。後面幾篇我會拆開講 constraint 怎麼展開、CQL 為什麼不能當授權語言、AI Agent 的 function tooling 怎麼收斂,還有 OPA 跟 Kafka 補進來之後,整張圖怎麼合起來。