Spring Cloud Gateway 中自定義驗證碼接口返回 404 的排查與解決
問題背景
在一個基于 Spring Cloud Gateway + WebFlux 構建的微服務項目中,新增了一個本地驗證碼接口 /code
,使用函數式路由(RouterFunction
)和 Hutool 的 CircleCaptcha
生成驗證碼圖片。然而在部署上線后,訪問該接口始終返回 404
,而其他網關轉發接口均正常。
該接口定義如下:
@Configuration
public class RouterFunctionConfiguration {@Autowiredprivate ValidateCodeHandler validateCodeHandler;@Beanpublic RouterFunction<?> routerFunction() {return RouterFunctions.route(RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),validateCodeHandler);}
}
初步排查思路
起初懷疑為以下常見問題:
Accept: text/plain
請求頭不匹配;- 路由被
gateway.routes
中/code/**
路由轉發覆蓋; RouterFunction
Bean 未生效或未注入;ValidateCodeHandler
寫法錯誤;- 實際監聽端口與預期不符(注冊到 Nacos 的端口為 8080)。
經多項驗證后發現,這些都不是直接原因。
真正的原因:JVM 圖形字體系統缺失導致驗證碼生成失敗
查看日志后,發現關鍵錯誤如下:
java.lang.NoClassDefFoundError: Could not initialize class sun.font.SunFontManagerat sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:264)...at cn.hutool.captcha.CircleCaptcha.createImage(...)
Hutool 的 CircleCaptcha
使用 Java AWT 圖形庫渲染驗證碼,而 AWT 依賴系統字體和圖形環境支持。在當前使用的 Docker 鏡像 openjdk:8-jre-slim
中,字體系統缺失,導致:
- JVM 無法初始化字體管理器;
Captcha.createImage()
方法拋出NoClassDefFoundError
;- WebFlux 返回 500,但表現為
/code
接口 404(無默認異常處理器); - 外部看起來像是“接口路由失效”。
解決方案
方案一:更換基礎鏡像(推薦)
將 Dockerfile 中基礎鏡像改為完整版本:
FROM openjdk:8-jre # 或 openjdk:8-jdk
該版本包含 AWT 和字體支持,開箱即用。
方案二:在 slim 鏡像中安裝字體依賴
如果仍想使用 slim
鏡像,可在構建中添加字體:
RUN apt-get update && apt-get install -y fontconfig ttf-dejavu
或者更小:
RUN apt-get update && apt-get install -y fonts-dejavu-core
這會安裝 JVM 字體渲染所需的核心字體文件和環境。
方案三:替換驗證碼實現邏輯
如果無需圖形驗證碼,可以:
- 改用字符驗證碼;
- 替換為不依賴 AWT 的實現;
- 或直接生成文本驗證碼。
驗證建議
1. 使用 curl
添加請求頭進行測試:
curl -H "Accept: text/plain" http://localhost:8001/code
2. 日志開啟 Web 路由匹配調試:
logging:level:org.springframework.web: DEBUG
總結
問題 | 原因 |
---|---|
/code 接口 404 | 實際是內部拋出 NoClassDefFoundError ,未進入 handler |
報錯類 | sun.font.SunFontManager 無法初始化 |
根本原因 | Docker 鏡像缺少字體渲染環境 |
解決方式 | 更換鏡像或安裝字體 |
建議
在使用 Hutool、Captcha、或任何基于 AWT 的組件時,務必檢查部署環境是否具備必要圖形支持。特別是在 Docker 化部署中,openjdk-slim
鏡像雖小,但常缺關鍵功能,適用場景需慎重評估。