keycloak接入飛書時中文亂碼解決及思考

附飛書provider地址:https://github.com/tedgxt/keycloak-service-social-lark

場景介紹

在keycloak 8.0.1版本中添加飛書的identity provider,實現基于飛書的第三方免登陸授權。

問題現象

在調用飛書接口獲取用戶信息時,http response打印出來的名字信息(中文)是亂碼。

確定問題邊界

步驟一

由于keycloak的框架中,封裝了一個SimpleHttp對象,用于http調用。因此得先判斷下http client是否該存在問題。

首先通過Postman去調用相同接口,得到response的數據如下。可以看到在postman里顯示的中文名字是正常顯示的,且response的header中,Content-Type的值為application/json; charset=utf-8

{
    "code": 0,
    "data": {
        "email": "xiatao.guan@longbridge.sg",
        "en_name": "希夏(管峽濤)",
        "expires_in": 6900,
        "mobile": "+8615968878843",
        "name": "希夏(管峽濤)",
        "open_id": "ou_a2eba3085ce4b126891bcca48fe23eb3",
        "refresh_expires_in": 2591700,
        "refresh_token": "ur-D99OWLBARc3ZlD1Js0yqSg",
        "tenant_key": "2c81b678d5cf975e",
        "token_type": "Bearer",
        "union_id": "on_b2455e0574333e08a0ecea8b932deac9",
        "user_id": "fg7fe7f5"
    },
    "msg": "success"
}

步驟二

更進一步,在idea中新開一個java測試工程,導入keycloak相關的maven依賴包,通過keycloak的SimpleHttp client進行接口調用,發現也可以打印出正常的中文。

  String profileStr = SimpleHttp.doGet(PROFILE_URL, httpClient).auth(accessToken).asString();
  logger.info(profileStr);

步驟三

在keycloak代碼中,添加測試代碼logger.info("中文")以及System.Out.Println("中文"),查看日志中打印出來的中文也是亂碼。

定位結果

綜合以上定位過程可以發現,在keycloak的中文字符都不能正常顯示。基本可以判斷亂碼問題與飛書的http調用無關,造成異常的原因應該在運行環境上

通過對比keycloak和測試工程的環境發現,keycloak本身是運行在jboss中的,而測試工程里代碼是跑在idea啟動的JVM上。下一步就是要解決jboss的編碼問題。

通過Google發現,要解決JVM中的中文編碼問題,可以通過配置參數-Dfile.encoding=XXX在啟動JVM的時候來指定編碼格式,從而不受操作系統和語言環境的影響。file.encoding 會影響未指定編碼的字符串、讀寫文件、URL編碼、打印等內容。

在jdk源碼中搜索file.encoding,發現被defaultCharset()方法使用了,打開對應文件Charset.java

    /**
     * Returns the default charset of this Java virtual machine.
     *
     * <p> The default charset is determined during virtual-machine startup and
     * typically depends upon the locale and charset of the underlying
     * operating system.
     *
     * @return  A charset object for the default charset
     *
     * @since 1.5
     */
    public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                String csn = AccessController.doPrivileged(
                    new GetPropertyAction("file.encoding"));
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }

這段注釋說明了defaultCharset返回的是JVM的默認編碼格式。且該編碼格式是在jvm啟動時便確定了,且依賴于jvm所在的操作系統的區域以及字符集。從代碼中可以看出,defaultCharset()的值就是從 file.encoding這個屬性中獲取的。

于是在keycloak代碼中加入如下代碼段,打印出當前的編碼配置:

// fix 中文字符問題
Properties initProp = new Properties(System.getProperties());
logger.info("current file.encoding:" + initProp.getProperty("file.encoding"));
logger.info("current Charset.defaultCharset :" + Charset.defaultCharset());

根據打印結果顯示,file.encodingCharset.defaultCharset打印的值都是ASCII,并非期望的UTF-8,這就解釋了為何打印中文在日志中顯示的都是亂碼。

解決過程

知道了要改的內容,接下來解決的過程就比較簡單了。通過Google搜索到,jboss是使用standalone.xml作為其啟動配置文件。在路徑/opt/jboss/keycloak/standalone/configuration中找到了該文件。按照網上的方法,在standalone.xml添加了相關編碼配置,然而打開日志一看并沒有作用。

于是逐步分析keycloak的啟動過程,通過對keycloak的dockerfile層層抽絲剝繭,發現entrypoint調用了standalone.sh,在standalone.sh中又調用了standalone.conf

bash-4.4$ cat standalone.sh|grep standalone
# Usage : standalone.sh --debug
#         standalone.sh --debug 9797
    RUN_CONF="$DIRNAME/standalone.conf"
...
...
...
# Read an optional running configuration file
if [ "x$RUN_CONF" = "x" ]; then
    RUN_CONF="$DIRNAME/standalone.conf"
fi

再一次Google一下jboss的相關資料,發現jboss是使用standalone.xml作為默認配置,而把standalone.conf作為配置首選項(Linux環境中是standalone.conf,Windows環境是standalone.bat)。這也就說明了為什么修改了standalone.xml后,并沒有起作用,還需要檢查standalone.conf的配置。

The standalone startup script i.e. *standalone.sh* for OSX/Linux and *standalone.bat* for Windows, utilizes:

- *standalone.conf/standalone.conf.bat*: defines the JVM preferences for the standalone server instance
- *standalone.xml*: defines the default configurations for the server; we can find it under *$JBOSS_HOME/standalone/configuration*.

因此在standalone.conf中添加上配置JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8",重新構建keycloak鏡像并啟動容器。

在未添加JVM的編碼格式時,keycloak的啟動日志中JAVA_OPTS如下:

=========================================================================

 JBoss Bootstrap Environment

 JBOSS_HOME: /opt/jboss/keycloak

 JAVA: java


 JAVA_OPTS:  -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED

=========================================================================

指定了file.encoding=UTF-8后,keycloak的啟動日志中可以看到JAVA_OPTS中編碼格式為UTF-8

=========================================================================

 JBoss Bootstrap Environment

 JBOSS_HOME: /opt/jboss/keycloak

 JAVA: java

 JAVA_OPTS:  -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dfile.encoding=UTF-8  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED

=========================================================================

打開日志,發現已經可以打印出中文,大功告成。

總結思考

對keycloak、jboss以及jvm基本上算是小白,看到編碼問題更是慌了手腳,最終花了一整天的時間才徹底解決這個問題。回想起來,解決該問題的根本因素就是jvm的編碼配置。

總結一下幾點事后經驗:

  1. 碰到未知問題,首先確定問題邊界,縮小問題定位范圍。如用postman調用飛書接口排除飛書server端問題;用logger打印中文字符排除http調用的問題。

  2. 要敢于了解系統原理,最好直接去閱讀相關源碼(時間充足的情況)。本項目最初預期只是寫點java代碼去開發一個keycloak的provider用于對接飛書,開發、解決問題的過程中卻額外涉及了編碼、jboss、jvm等新知識,人總是喜歡蹲在熟悉的環境中,為了解決問題只好硬著頭皮上了。

  3. 構建知識領域地圖,不斷補全相關知識漏洞。如果對jvm、jboss一開始就很熟悉,想必解決問題的時間可以減半;知識掌握的越多,后續遇到新問題的解決過程就會越順利。

  4. Google大法好!

參考資料

  1. 深入分析 Java 中的中文編碼問題

  2. Java| Charset.defaultCharset()和file.encoding的關系

  3. JBoss Server – How to Start and Stop?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容