本文介紹了通過(guò)反射訪問(wèn)HttpURLConnection的私有MessageHeader字段(&Q;Autorization&Q;)的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!
問(wèn)題描述
目前正在為現(xiàn)有庫(kù)編寫單元測(cè)試,我正在嘗試解決here的限制,即您無(wú)法使用反射檢索已設(shè)置的授權(quán)和標(biāo)頭。
我使用的代碼是一個(gè)非常典型的代碼片段,我已經(jīng)使用了幾十次來(lái)訪問(wèn)私有字段:
HttpURLConnection conn = (HttpURLConnection) new URL("https://stackoverflow.com").openConnection();
conn.setRequestProperty("Authorization", "Basic Zm9vYmFyOnNlY3JldA==");
try {
Field requests = conn.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredField("requests");
requests.setAccessible(true);
MessageHeader headers = (MessageHeader) requests.get(conn); // Problem: returns null
return headers.getValue(headers.getKey("Authorization"));
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
但是-通過(guò)Field::get提取失敗并返回null(請(qǐng)參閱注釋行)。
查看HttpUrlConnection的基類URLConnection,我知道我正在尋找requests字段。通過(guò)它調(diào)試,我可以看到我想要提取的字段(甚至顯示了授權(quán)和值):
在無(wú)法返回MessageHeader對(duì)象的代碼行中,看起來(lái)我引用了URLConnection:
中的字段
但我肯定遺漏了什么–有人能說(shuō)出是什么嗎?
更新
讓我困惑的是
-
我只從
java.net包導(dǎo)入URLConnection和HttpURLConnection這一事實(shí)。然而,從第一個(gè)調(diào)試屏幕截圖來(lái)看,conn-對(duì)象實(shí)現(xiàn)顯然來(lái)自sun.net.www.protocol.https。DelegateHttpsURLConnection成員(也顯示在第一個(gè)調(diào)試截圖中)
推薦答案
當(dāng)您在對(duì)象上調(diào)用getClass()時(shí),您將獲得其實(shí)際的運(yùn)行時(shí)類型,該類型不必與編譯時(shí)類型匹配,因?yàn)樗赡苁撬淖宇悺R虼耍ㄟ^(guò)getSuperclass()遍歷類層次結(jié)構(gòu)是很脆弱的。
當(dāng)您知道預(yù)先聲明了字段的類時(shí),您可以只使用該類,例如
HttpURLConnection conn = (HttpURLConnection)
new URL("https://stackoverflow.com").openConnection();
try {
Field f = URLConnection.class.getDeclaredField("requests");
f.setAccessible(true);
System.out.println(f.get(conn));
}
catch(ReflectiveOperationException ex) {
ex.printStackTrace();
}
但是,這將始終打印null,因?yàn)檫@個(gè)特定的連接實(shí)現(xiàn)不使用其超類狀態(tài),而是委托給不同的實(shí)現(xiàn)。正如您自己發(fā)現(xiàn)的那樣,它存儲(chǔ)在一個(gè)名為delegate的字段中。
HttpURLConnection conn = (HttpURLConnection)
new URL("https://stackoverflow.com").openConnection();
for(Class<?> c = conn.getClass(); c != URLConnection.class; c = c.getSuperclass())
System.out.print(c.getName() + " > ");
System.out.println(URLConnection.class.getName());
Field delegate = conn.getClass().getDeclaredField("delegate");
delegate.setAccessible(true);
conn = (HttpURLConnection)delegate.get(conn);
for(Class<?> c = conn.getClass(); c != URLConnection.class; c = c.getSuperclass())
System.out.print(c.getName() + " > ");
System.out.println(URLConnection.class.getName());
Field requests = URLConnection.class.getDeclaredField("requests");
requests.setAccessible(true);
System.out.println(requests.get(conn));
此打印
sun.net.www.protocol.https.HttpsURLConnectionImpl > javax.net.ssl.HttpsURLConnection > java.net.HttpURLConnection > java.net.URLConnection
sun.net.www.protocol.https.DelegateHttpsURLConnection > sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection > sun.net.www.protocol.http.HttpURLConnection > java.net.HttpURLConnection > java.net.URLConnection
null
顯示這兩個(gè)對(duì)象具有不同的類層次結(jié)構(gòu),因此,假設(shè)特定深度遍歷它確實(shí)是脆弱的。
它還會(huì)再次打印null,因?yàn)榧词故沁@個(gè)實(shí)現(xiàn)類也沒有使用從URLConnection繼承的requests字段。相反,類sun.net.www.protocol.http.HttpURLConnection聲明了自己的同名字段,其中包含實(shí)際的MessageHeader對(duì)象。這就是為什么在委托的實(shí)現(xiàn)類上執(zhí)行getSuperclass().getSuperclass()會(huì)得到正確的字段,而不是URLConnection的字段。
當(dāng)我們事先知道這一點(diǎn)時(shí),我們可以使用
HttpURLConnection conn =
(HttpURLConnection) new URL("https://stackoverflow.com").openConnection();
conn.setRequestProperty("Authorization", "Basic Zm9vYmFyOnNlY3JldA==");
Field delegate = Class.forName("sun.net.www.protocol.https.HttpsURLConnectionImpl")
.getDeclaredField("delegate");
Field requests = Class.forName("sun.net.www.protocol.http.HttpURLConnection")
.getDeclaredField("requests");
AccessibleObject.setAccessible(new Field[] { delegate, requests}, true);
sun.net.www.MessageHeader headers =
(sun.net.www.MessageHeader)requests.get(delegate.get(conn));
return headers.findValue("Authorization");
因?yàn)槟鸁o(wú)論如何都要訪問(wèn)sun.net. …包,所以您可以直接引用這些類,而不是使用Class.forName,但我認(rèn)為后者在這種特定情況下更可靠,因?yàn)樗羞@些類都很容易混淆名稱,有時(shí)甚至是相同的簡(jiǎn)單名稱,只是它們的包不同。
private String getAuthorizationHeaderValue(HttpURLConnection conn) {
try {
Field delegate = sun.net.www.protocol.https.HttpsURLConnectionImpl.class.getDeclaredField("delegate");
Field requests = sun.net.www.protocol.http.HttpURLConnection.class.getDeclaredField("requests");
AccessibleObject.setAccessible(new Field[] { delegate, requests}, true);
sun.net.www.MessageHeader headers = (sun.net.www.MessageHeader)requests.get(delegate.get(conn));
return headers.findValue("Authorization");
} catch(ReflectiveOperationException ex) {
ex.printStackTrace();
return "";
}
}
這篇關(guān)于通過(guò)反射訪問(wèn)HttpURLConnection的私有MessageHeader字段(&Q;Autorization&Q;)的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,






