程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Web服務: WS-Security的大開銷

Java Web服務: WS-Security的大開銷

編輯:關於JAVA

WS-Security 以現有的密碼學以及 XML 加密和簽名行業標准為基礎,為 Web 服務應用程序提供了一組全面的安全特性,您可以通過 WS-Policy 和 WS-SecurityPolicy 來指定特定應用程序可以使用哪些特性,從而允許服務客戶機自行配置以訪問服務。通過跨多個平台和 Web 服務框架對這些標准的廣泛支持,可以實現出色的互操作性(並且會不斷改善)。

盡管能帶來這麼多好處,但 WS-Security 也存在一些缺點。在本 系列 的前兩篇文章中,您已經知道 WS-Security 的配置有時會非常復雜,並且有時會在交換的消息中添加許多塊(bulk)。那麼,WS-Security 帶來的收益在什麼情況下才物有所值呢?在本文中,我們將深入探討 WS-Security 以及相關 WS-SecureConversation 的運行時成本(在處理開銷和添加塊方面),並引申到如何應用 WS-Security 才能讓應用程序受益的話題。

觀察性能

為了測量應用程序在不同配置下的性能,本文將測定當客戶機和服務器運行於同一系統中特定請求序列的執行時間。這種方法存在一些缺點 — 最顯著的是,它將客戶機和服務器處理開銷結合在一起,因此不能單獨測算它們 — 但它比在網絡上運行測試能生成更加一致的結果。您還可以輕松地在自己的硬件和 JVM 上試運行這些測試,相關的實現代碼請參見 下載。

性能測試應用程序

用於測試的應用程序是一個地震數據檢索服務。它基於一個地震數據庫,其中包含一段時間內世界各地發生的 93,000 多次地震的實際數據。對服務的請求將指定經度、緯度、日期或震級的范圍,並且服務將按地區和時間順序分組返回所有匹配的地震。整個數據庫按索引保存在內存中,以便於快速處理請求,因此每條請求幾乎所有的處理時間都花在實際的 Web 服務處理代碼上(包括將轉換為 XML 或從 XML 轉換而來的數據綁定代碼)。

清單 1 展示了一個對服務的示例請求,以及隨後的響應(為適應頁面寬度重新調整了格式):

清單 1. 示例請求和響應

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
   <ns1:matchQuakes xmlns:ns1="http://ws.sosnoski.com/seismic/types">
    <ns1:min-date>2001-08-08T16:31:05.752+00:00</ns1:min-date>
    <ns1:max-date>2001-08-14T23:51:31.499+00:00</ns1:max-date>
    <ns1:min-long>160.4685</ns1:min-long>
    <ns1:max-long>178.19693</ns1:max-long>
    <ns1:min-lat>-42.423557</ns1:min-lat>
    <ns1:max-lat>-30.44976</ns1:max-lat>
   </ns1:matchQuakes>
  </soapenv:Body>
</soapenv:Envelope>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
   <ns1:results xmlns:ns1="http://ws.sosnoski.com/seismic/types" count="9">
    <ns1:result-set>
     <ns1:area-name>New Zealand Region</ns1:area-name>
     <ns1:regions count="0">
      <ns1:region ident="rgn159" index="159">NORTH ISLAND,
        NEW ZEALAND</ns1:region>
      <ns1:region ident="rgn160" index="160">OFF E. COAST OF N. ISLAND,
        N.Z.</ns1:region>
     </ns1:regions>
     <ns1:quakes count="9">
      <ns1:quake time="2001-08-11T09:52:54.000+00:00" millis="1000"
        latitude="-37.6499" longitude="177.74" depth="83.0" magnitude="4.4"
        method="ML" region="rgn160"/>
      <ns1:quake time="2001-08-11T09:52:55.000+00:00" millis="0"
        latitude="-37.71" longitude="177.77" depth="70.0" magnitude="4.5"
        method="ML" region="rgn160"/>
      <ns1:quake time="2001-08-11T15:02:47.000+00:00" millis="5600"
        latitude="-38.0429" longitude="175.632" depth="299.8" magnitude="4.6"
        method="ML" region="rgn159"/>
      <ns1:quake time="2001-08-12T07:42:41.000+00:00" millis="7000"
        latitude="-37.97" longitude="175.97" depth="289.0" magnitude="4.3"
        method="MB" region="rgn159"/>
      <ns1:quake time="2001-08-12T22:37:58.000+00:00" millis="5600"
        latitude="-38.3839" longitude="176.121" depth="163.2" magnitude="4.0"
        method="ML" region="rgn159"/>
      <ns1:quake time="2001-08-12T23:25:09.000+00:00" millis="6700"
        latitude="-39.9559" longitude="176.115" depth="76.0" magnitude="4.0"
        method="ML" region="rgn159"/>
      <ns1:quake time="2001-08-13T05:10:07.000+00:00" millis="4300"
        latitude="-37.5859" longitude="176.651" depth="189.0" magnitude="4.3"
        method="ML" region="rgn159"/>
      <ns1:quake time="2001-08-14T02:43:18.000+00:00" millis="2900"
        latitude="-38.3699" longitude="175.902" depth="193.4" magnitude="4.5"
        method="ML" region="rgn159"/>
      <ns1:quake time="2001-08-14T18:02:35.000+00:00" millis="5400"
        latitude="-37.8159" longitude="176.375" depth="193.3" magnitude="4.5"
        method="ML" region="rgn159"/>
     </ns1:quakes>
    </ns1:result-set>
   </ns1:results>
  </soapenv:Body>
</soapenv:Envelope>

在測試中,客戶機將查詢范圍調整為整體地震數據集的一部分,並生成一系列偽隨機請求。每次使用相同輸入參數運行客戶機所生成的請求序列都是相同的,這允許我們測試不同的 Web 服務配置。通過更改客戶機的輸入參數(用於更改請求所使用的查詢范圍),可以輕松地測試不同的結果消息大小。

WS-Security 性能

本文所示的測試結果基於兩個請求序列。第一個序列使用 1,000 條請求,查詢參數經過調整以便各查詢只匹配整個地震數據庫的一小部分(1,000 條請求僅返回 826 次匹配的地震)。第二個序列使用 100 條請求,同時調整以匹配更大的數據庫部分(100 條請求返回 176,745 次匹配的地震)。每個請求序列都在不同的安全性配置下運行了多次,每種配置只取最好的測試結果。

運行測試的環境是 Mandriva 2009.1 64-bit Linux® 系統、Athlon X2 5400+ 處理器、4GB 內存和 Sun Java 1.6.0_13 32 位 JVM。服務器代碼在 Tomcat 6.0.20 上運行,配置為使用 1,024MB 大小的堆,客戶機代碼使用 512MB 大小的堆。我們使用 1.5 版本的 Axis2,並使用最新版本的 Rampart 代碼。(在本文測試時,還沒有與 Axis2 1.5 代碼相匹配的 Rampart 發行版可用。要運行完整的測試集,為 Tomcat 配置 1,024MB 大小的堆是非常必要的(為各安全性配置使用單獨的 Web 服務應用程序);剛開始使用 256MB 大小的堆執行測試時,WS-Security 測試有時會因為各種奇怪的錯誤(舉例來說,未提供 DTD 時出現的 “SOAP message MUST NOT contain a Document Type Declaration(DTD)” 錯誤)或者 java.lang.OutOfMemoryError 而失敗。

我們使用以下安全性配置運行各測試:

plain:無安全性

ssl:使用 HTTPS 連接到服務器

username:請求中使用 WS-Security 純文本 UsernameToken

sign:WS-Security 主體和報頭簽名,使用時間戳

encr:WS-Security 主體加密

signencr:WS-Security 主體和報頭簽名,使用時間戳和主體加密

實際測試時間從 plain 配置的 4 秒到 signencr 配置的 55 秒。圖 1 顯示了相對測試時間,為便於比較使用了相對 plain 配置時間的倍數:

圖 1. 測試時間比較

從 圖 1 中可以看出,Secure Sockets Layer (SSL) — 從技術上說,現在應該稱作 Transport Layer Security (TLS),但本文仍然使用為人所熟知的舊表示方法 — 加密所提供的性能接近無保護措施時的性能水平(但其處理大消息比處理小消息的性能要好,處理小消息所花的時間要長 80%,處理大消息所花的時間要長 20%)。另一方面,使用 WS-Security 會造成性能顯著降低。僅在請求消息上添加簡單的 UsernameToken 報頭會造成性能降低到 SSL 處理小消息時的性能水平,但比使用 SLL 處理大消息時的性能慢 幾倍。在簽名與加密相結合的情況下,測試時間比無保護措施下要長 2,100%。

從一定程序上說,WS-Security 帶來的這種性能上的影響歸因於 Rampart 處理程序實現的缺陷,這會造成每次有 Rampart 參與時都將各請求和響應消息轉換成 Document Object Model (DOM) 格式(即使未對消息執行任何安全性處理)。應該在 Rampart 1.5 發行版中修復此問題以便它可以兼容 Axis2 1.5。根據修復的實現方式,它可以顯著改善 UsernameToken 測試的運行時間。但是,即使修復了此問題可能也不會影響其他的 WS-Security 運行時間。

與 WS-Security 相結合的 XML Signature 和 XML Encryption 的運行方式,以及 WS-Security 和這些 XML 標准的實現所使用的庫也是影響性能的因素之一。還記得 “Java Web 服務:Axis2 WS-Security 簽名和加密” 這篇文章說過,簽名 XML 消息需要一個叫做規范化 的步驟,用於在捕獲簽名值之前將 XML 轉換為特定的規范格式。標准需要這一步驟是因為已經確定即使在解析器分解 XML 並重新生成它之後也需要保留簽名。雖然這毫無疑問是 XML Signature 的一個實用的特性,但它為處理增加了大量的開銷。在一定程度上,由於使用 DOM 模型是實現規范化最簡單的一種方法,因此 XML 安全性庫都被設計為操作 XML 的 DOM 表示。(這是為何 Rampart 處理程序即使在參與服務或客戶機任務時也要生成 DOM 的原因,但前提是 DOM 有必要的作用)。僅將數據轉換成 DOM 表示的步驟就會造成大量的 WS-Security 開銷,這可以從 UsernameToken 時間看出。在大響應消息的情況中,這種開銷看上去與實際的簽名或加密處理相當(如 圖 1 所示,比較 username 測試的紅柱 — 其中,唯一的主要開銷是 DOM 的創建 — 與 sign 和 encr 測試的紅柱,其中,實際的加密處理是在創建 DOM 之後完成的)。

除了 DOM 問題之外,大部分 WS-Security 開銷都是計算緊密型的生成摘要和加密數據的任務。這一部分的工作是必需的,而與所使用的實現方法無關,因此 WS-Security 處理時間的改善是有限的。本系列的後續文章將比較其他一些使用 Axis2/Rampart 的 WS-Security 實現的性能 — 但是,它們大多數都使用相同底層庫,因此不要指望能看到巨大的差異。

WS-SecureConversation

WS-SecureConversation 是在 WS-Security 和 WS-Trust 標准之上構建的一種標准,用於支持涉及多個消息的安全交換。由於它所使用的上下文針對運行中的消息交換,因此 WS-SecureConversation 更有可能比 WS-Security 實現更高的效率。Rampart 發行版包括一個單獨的 rahas 模塊,它支持發起 WS-SecureConversation 所需的安全令牌。它還包括一個使用 WS-SecureConversation 的策略配置示例(samples/policy/sample04),用於作為性能測試應用程序所使用的策略基礎。

WS-SecureConversation 策略(未包含在此處,請參見 下載 中的 secureconversation-policy-client.xml)包括一個 <sp:SecureConversationToken> 元素,用於描述消息交換將使用的安全令牌,以及提供應用於 token-exchange 消息的安全性選項。這些 token-exchange 消息使用由 rahas 模塊實現的操作為客戶機和服務之間的消息交換提供支持 — 因此在使用 WS-SecureConversation 時,您偶爾會看到 request-response 消息對在客戶機和服務器之間傳遞,如 清單 2 所示。要區分添加的這些 token-exchange 消息與應用程序消息,可以根據它們所使用的不同的安全性選項(由策略定義),以及它們所使用的特殊的 http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT 請求和 http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT 響應操作代碼(兩者都由 WS-SecureConversation 定義)。

清單 2. 示例請求和響應

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
  <soapenv:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
   <wsse:Security xmlns:wsse="http://...-wss-wssecurity-secext-1.0.xsd"
     soapenv:mustUnderstand="1">
    ...
   </wsse:Security>
   <wsa:To
   >http://localhost:8800/axis2/services/seismic-secureconversation</wsa:To>
   <wsa:ReplyTo>
    <wsa:Address
    >http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
   </wsa:ReplyTo>
   <wsa:MessageID>urn:uuid:5EA8E8F04EBA73255B1246409570148</wsa:MessageID>
   <wsa:Action>http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</wsa:Action>
  </soapenv:Header>
  <soapenv:Body xmlns:wsu="http://...-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="Id-30222347">
   <xenc:EncryptedData ...>
    ...
   </xenc:EncryptedData>
  </soapenv:Body>
</soapenv:Envelope>

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
   <wsse:Security xmlns:wsse="http://...-wss-wssecurity-secext-1.0.xsd"
     soapenv:mustUnderstand="1">
    ...
   </wsse:Security>
   <wsa:To
   >http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
   <wsa:MessageID>urn:uuid:1BCDE6BE423F5FDE791246409571325</wsa:MessageID>
   <wsa:Action
   >http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT</wsa:Action>
   <wsa:RelatesTo>urn:uuid:5EA8E8F04EBA73255B1246409570148</wsa:RelatesTo>
  </soapenv:Header>
  <soapenv:Body xmlns:wsu="http://...-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="Id-5148380">
   <xenc:EncryptedData ...>
    ...
   </xenc:EncryptedData>
  </soapenv:Body>
</soapenv:Envelope>

為了與普通的使用證書的 WS-Security 的性能相比較,WS-SecureConversation 配置被設置為僅在加密消息主體時使用會話令牌。圖 2 展示了最終測試時間與 plain 及 encr 測試配置之間的比較,為便於比較使用了相對 plain 配置時間的倍數:

圖 2. WS-SecureConversation 時間比較

如 圖 2 所示,WS-SecureConversation 加密確實相對 WS-Security 提供顯著的性能改善。這對於較小的消息尤為明顯,其 WS-SecureConversation 配置的運行速度幾乎是使用證書的 WS-Security 加密的兩倍。而對於較大的消息來說,性能優勢則遜色許多,但 WS-SecureConversation 仍然提供了 18% 的速度提升。

消息大小比較

從系列的前一篇文章中看到,WS-Security 可以會向 SOAP 消息報頭添加大量塊(bulk)。當數據通過網絡在客戶機與服務器之間傳遞時,這些添加的塊會對性能造成顯著影響(而在本文中,客戶機和服務器是在相同的系統上運行的)。客戶機與服務之間的網絡連接的質量將決定這些增加的塊會對性能造成多大的影響,但毫無疑問的是,消息越大,交換速度就越慢。

圖 3 顯示了不同測試用例中的典型消息的實際大小,為便於比較使用了相對 plain 配置時間的倍數:

圖 3. 消息大小比較

與預期相符,username 配置僅增加了請求消息的大小,因為 UsernameToken 僅出現在請求消息中。其他安全性配置則同時增加了請求和響應消息的大小。WS-Security 添加的塊對於較小的請求和響應消息來說更加明顯。對於各配置來說,WS-Security 報頭基本上是恆定不變的,因此當原始消息大小較小時,相同大小的增加會帶來更加顯著的影響。在使用加密時,加密數據所使用的 base64 編碼中出現了單獨的填充(padding)效果。

何時使用 WS-Security

您已經了解了 WS-Security 對處理時間和消息大小的影響。您可能想知道這些代價何時才是值得的。簡單的回答是:當較簡單的 SSL 替代方案沒有作用時。在本節的其余部分,我們將提供一些指導方針來幫助您確定 SSL 是否能滿足您的需求,或者是否需要全功能的 WS-Security 解決方案,並且還將指導您在選擇 使用 WS-Security 後最大限度降低與之相關的開銷。

保密

保護機密信息是安全性最重要的一個方面。WS-Security 使用 XML Encryption 保護消息內容不被非目標接收者獲取,其方法通常是使用封裝在數字證書中的公共密匙。加密可以應用於整個消息或所選部分,並且您甚至可以使用多層加密來控制哪些信息可以由涉及多個系統的消息交換中的參與者訪問。

WS-Security 的一個示例用例是一個在線購物系統,其中,客戶機連接到商業系統下訂單,但付款(比如說,通過信用卡)需要在處理訂單前由某銀行系統確認。如果商業系統能夠以未加密的形式訪問信息卡信息,則會帶來基於浏覽器的購物網站所特有的安全性風險:信用卡最終經常會存儲在安全性較差、易於受到黑客攻擊的數據庫中。使用 WS-Security,信用卡信息可以以加密的格式傳遞,並且僅能由發出付款確認的銀行系統解密。

但是,對於許多應用程序來說,在保護機密信息方面,WS-Security 和 XML Encryption 的功能有點過於強大。如果您的服務需要客戶直接與之聯系(而不是間接通過其他服務器)並且直接執行所有必要的處理,那麼您可以僅使用 SSL (HTTPS) 連接來實現訪問功能,這樣能以比 WS-Security 更低的成本提供出色的機密信息保障功能。不過,這種方法僅適用於直接客戶機連接。否則,如果服務需要將信息傳遞給其他服務,那麼您又遇到了與在線購物網站易受攻擊的情況:您使用安全的連接向購物網站傳遞信用卡信息,但是卻無法保證信息將安全地保存在站點服務器上。

維持數據完整性

數據完整性是與機密性相類似的一個問題。WS-Security 使用 XML Signature 確保數據不會在傳輸中被篡改,因為任何竄改都會導致簽名失效。與 XML Encryption 相同,簽名可以應用於整個消息或者所選部分,並且您可以使用多層簽名來保證涉及多個系統的消息交換中的參與者所處理的數據的完整性。

虛構的在線購物系統還提供了很好的數據完整性的例子。使用 WS-Security,客戶對發送給銀行系統的付款指令進行簽名,從而防止中間的商業系統隨意修改指定的付款額。由於付款額經過客戶簽名,因此不需要對它進行加密,以便商業系統在向銀行系統提交事務之前確定付款額正確無誤。

此處,如果您的服務需要客戶直接與之連接並且在內部執行所需必需的處理,則 WS-Security 和 XML Signature 又顯得有些過於強大了。SLL 連接不僅能保證數據的機密性,它們還能防止數據在傳輸過程中被修改 — 但僅限於在在單一的客戶機與服務器之間。如果服務器將數據傳遞給其他系統,則這些系統無法保證數據不被服務器修改。

確保真實性

在真實性方面,WS-Security 所提供的特性是 SLL 所無法匹及的,即使針對客戶機與服務器之間的直接連接。使用 WS-Security 和 XML Signature 對消息進行簽名,不僅允許您的文檔在接收和處理時進行真實性驗證,還可以為用於審計的文檔提供可靠的真實性保障。

SSL 在這方面做的出色的地方就是在客戶機與服務器之間建立連接時要求客戶機證書作為身份證明。但是,它所提供的真實性保障要比消息數字簽名弱很多。您不能輕易地將客戶機與服務器之間交換的整個數據流保存為 SSL 連接的一部分,因此,即使您在建立該連接時驗證了客戶機證書,也無法在稍後返回並證明此步驟已得到正確處理。

再次回到在線購物系統示例,付款指令上的客戶機簽名將允許該指令由銀行系統保存,並在以後就該事務出現任何爭辯時提供該簽名。這樣,銀行系統便可以證明 付款已經得到了客戶的授權,從而讓自己免除任何責任。

其他特性

除了基本的機密性、完整性和真實性之外,WS-Security 還針對特定的安全性需求提供了許多其他特性。舉例來說,UsernameToken 就是一個簡單的特性,它提供了與服務傳遞基本用戶授權的標准方法。其他 WS-Security 特性還允許 Security Assertion Markup Language (SAML) 授權令牌和其他形式的與安全性相關的信息,用於添加到 SOAP 消息交換中。如果需要在 Web 服務中使用此類型的安全性信息,可以使用 WS-Security 支持來傳遞信息,因為它定義了一些標准的格式和過程,可能會比您自己實現的功能更加可靠。

降低 WS-Security 的成本

如果您要使用 WS-Security,可以從本文的測試結果中看出性能是一個問題。但是,在顧慮其開銷之前,請考慮一下您服務的數據量。使用 WS-Security 進行加密和簽名之後,服務的性能會大打折扣 — 但是,如果您的服務每小時只交換少量消息,則硬件需求方面的差異是可以忽略不計的。

對於性能無法折衷的場景,您可以通過合理地安全性選項來幫助最大限度地減小性能影響。某些 Web 服務框架傾向於生成 “所有上述” 安全性配置,它們使用 WS-Security 實現全面的消息簽名和加密,並 通過 SSL 連接發送它們。如果您確實希望提供最高級別的保護,而對性能毫不關心,那麼這種方式沒有問題。但是,在大多數情況下,更有意義的方法是使用 SSL(如果只關心為在客戶機和單一服務器之間傳輸的信息提供保護)或者 WS-Security 加密(如果需要跨多個服務器傳遞數據,同時在它們經過中間系統時保護機密信息的安全。

對於客戶機和服務器之間需要長時間交換消息的情況工(甚至通過中間系統訪問其一),您還可以使用 WS-SecureConversation 提供相對於使用證書的 WS-Security 更好的性能。WS-SecureConversation 特別適合相對較小的消息之間的交換,其中,證書和非對稱加密方面增加的開銷與消息主體的實際(對稱)加密相比是相當可觀的。

降低 WS-Security 性能成本的另一種方法是將安全性處理轉嫁給專門的硬件來完成。一些 XML 網關工具提供了對 WS-Security 加密和簽名的加速處理。您可以使用這些工具來處理大開銷的 WS-Security,同時處理應用程序中的純 SOAP。顯然,您需要確保在向服務器添加工具時不會打開任何潛在的安全性漏洞。並且,您應該在購買之前測試工具所帶來的性能提升。但是,至少從理論上來說,這種模式確實能實現一些性能提升。

結束語

WS-Security 的性能成本是可觀的,並且簡單的 SSL 點對點加密有時是更好的解決方案。但是,對許多應用程序而言,SSL 是遠遠不夠的。在這些情況中,就必須使用 WS-Security(或者它的後代 WS-SecureConversation),而且性能成本也成為必要的開銷。在本文中,您已經了解了 WS-Security 的成本,並學習了一些可幫助您確定 WS-Security 是否適用於自己應用程序的指導方針。

在本系列的下一篇文章中,我們將從另一個視角來解讀 WS-Security,將演示如何使用 WS-SecurityPolicy 實現對 Web 服務中各操作所使用的 WS-Security 特性的粒度化控制。在操作層面控制 WS-Security 也是一個能夠(至少可能)降低 WS-Security 開銷的技巧,因此,在轉向其他話題之前,這是對本文的最好的沿續。

文章來源:

http://www.ibm.com/developerworks/cn/java/j-jws6/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved