Project

General

Profile

CR #225 » CR-024_服務間呼叫改採ServiceClientHttpClient.md

內容更新:內部稱謂去識別化(2026-10-18) - Redmine Admin, 10/18/2026 05:32 AM

 

CR-024 服務間呼叫改採 ServiceClientHttpClient(修除 Feign 繞過出站憑證之缺陷)

項目 內容
文件編號 CR-024
版本 1.0
提出日 2026-09-29
提出者 WorkBuddy AI(開發團隊)
緣起 GOV-006 §4 #21(節後開工之第一動,BEAM PMO 指示)。原記載為「LimitServiceClient(Feign)未接 M2M outbound token → 一律 401」,修法方向原建議「Feign 加 RequestInterceptor」;本次動工前查證後修正該建議(見 §2)
影響範圍 authorization-service:LimitServiceClient/CardServiceClient(刪除)、LimitServiceHttpClient/CardServiceHttpClient(新增)、RemoteLimitPort/RemoteCardPort/RemoteLimitUploadAdapter(改注入)、AuthorizationServiceApplication(移除 @EnableFeignClients)、build.gradle.kts(移除 Feign 依賴)
狀態 ✅ 已執行(2026-09-29)——編譯通過(BUILD SUCCESSFUL),真機驗證見 §5

1. 問題(#21 之實測證據)

現象(2026-09-29 真機實測,AUTH000041):

Stand-in 降級:limit-service 查詢失敗,改用本地讀模型。
  原因=feign.FeignException$Unauthorized: [401] …
Stand-in 降級:limit-service 佔用失敗,改由本地代佔(須事後上送)。
  原因=feign.FeignException$Unauthorized: [401] …

後果:

  1. 授權之額度佔用全數降級為本地代佔 → 決策恆為 STANDIN(無法產生 APPROVED);
  2. Stand-in 後送永遠 401 → standin_upload_at 恆為空 →
    #A-98(CR-022 (A) 後送)與 #A-99(/auth/stip 頁)兩項交付無法真正閉環;
  3. card-service 同型 → 卡片狀態檢核降級讀本地快取,日誌自身警告
    「注意:鎖卡可能未即時生效」——此為資安層級之降級。

2. ★ 查證:原建議之修法方向不正確(本 CR 之核心)

#21 原建議「Feign 加 RequestInterceptor 接 OutboundTokenProvider」。
動工前查證後修正,理由如下:

查證項 結果
平台是否已有服務間呼叫之共用客戶端? ✅ 有:common-spring 之 ServiceClientHttpClient(CSP-001 §8.5)
其如何處理憑證? 建構子強制要求非空之 OutboundTokenProvider;每次送出前取 authorizationHeaderValue() 並加 Authorization 標頭
其設計意圖? 註解明載:「杜絕『建了客戶端卻沒給憑證來源 → 靜默不帶標頭 → 全部 401』,與 CR-010 所欲修除之缺陷同型」
其他服務怎麼做? ✅ statement(×2)/payment/posting 皆用 ServiceClientHttpClient
誰用 Feign? ⚠️ 僅 authorization-service(×2)與 card-service(×1) —— 異類
authorization-service 之憑證機制有啟用嗎? ✅ 有——啟動日誌載「服務間呼叫憑證已啟用:client_credentials(client=cc-authorization-service)」

結論:憑證機制本來就是好的,問題純粹是 Feign 客戶端繞過了它。
若照原建議「Feign 加 interceptor」,將是重造 ServiceClientHttpClient 已實作之行為,
且失去其「強制非空」之保護(Objects.requireNonNull)——同型缺陷可能再犯。

→ 故改採:直接使用 ServiceClientHttpClient(與 4 個既有服務同構)。

3. 變更內容

檔案 動作
LimitServiceHttpClient 新增(ServiceClientHttpClient 之封裝;含 queryLimits/hold/release)
CardServiceHttpClient 新增(lookup;查無此卡改以 HTTP 404 判斷,原為 feign.FeignException.NotFound)
LimitServiceClient 刪除(Feign)
CardServiceClient 刪除(Feign)
RemoteLimitPort 改注入 LimitServiceHttpClient
RemoteCardPort 改注入 CardServiceHttpClient
RemoteLimitUploadAdapter 改注入 LimitServiceHttpClient(後送路徑)
AuthorizationServiceApplication 移除 @EnableFeignClients
build.gradle.kts 移除 spring-cloud-starter-openfeign 依賴

契約變更:無(端點與 DTO 不變,僅呼叫方式改變)。

失敗語意不變:所有遠端失敗仍拋 InfrastructureException(CC-PLT-5001),
降級決策仍統一由 StandInLimitPort/StandInCardPort 處理——本次未動降級邏輯。

4. 範圍說明(為何一併修 card-service)

#21 僅點名 LimitServiceClient,但 CardServiceClient 同服務、同型缺陷、同修法,
且其後果為資安層級(鎖卡未即時生效)。若只修一半,authorization-service
仍有一半呼叫 401 → 故一併修。

未動:card-service 之 CreditReviewClient(不同服務)——
已登記為後續項(同型,應以同一原則處理)。

5. 驗證

項 結果
編譯 ✅ BUILD SUCCESSFUL(含測試編譯)
單元測試 見 HANDOVER §3.A #A-100
真機驗證 見 HANDOVER §3.A #A-100:授權不再 401、決策為 APPROVED(非 STANDIN)、後送成功寫入 standin_upload_at

6. 未竟

  • ⚠️ card-service 之 CreditReviewClient(Feign)尚未處理——同型缺陷,已登記。
  • ⚠️ #21 原記載之「ACL 白名單須同步」——本次未動 ACL(實測未出現 403,故無須變更);
    若後續出現 403,再依 ServiceAcl 處理。

7. 修訂記錄

版本 日期 說明
1.0 2026-09-29 初版:#21 實測證據;★ 查證修正原建議之修法方向(Feign+interceptor → ServiceClientHttpClient);變更清單;範圍說明(一併修 card);驗證與未竟
    (1-1/1)