CR #225 » CR-024_服務間呼叫改採ServiceClientHttpClient.md
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] …
後果:
- 授權之額度佔用全數降級為本地代佔 → 決策恆為
STANDIN(無法產生APPROVED); -
Stand-in後送永遠 401 →standin_upload_at恆為空 →
#A-98(CR-022(A) 後送)與#A-99(/auth/stip頁)兩項交付無法真正閉環; -
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);驗證與未竟 |