JMX+J2SE5.0實現Web應用的安全管理

來源:互聯網
上載者:User
j2se|web|安全    一、 引言

  JMX(Java管理擴充)提供了一組工具用來管理本地和遠程應用程式、系統對象、裝置等。本文將解釋如何使用JMX(JSR 160)來遠端控制web應用程式,並將解釋應用程式中可用於JMX客戶的代碼,同時將展示使用如MC4J和jManage等的不同客戶如何串連到支援JMX的應用程式。此外,我們還將詳細地討論使用RMI協議和JNDI來保護通訊層。

  首先我們要分析一個簡單的web應用程式,它監控已經登陸的使用者數目並通過一個安全的JMX服務來顯示該項統計。我們還將運行這個應用程式的多個執行個體並且從所有的運行執行個體中跟蹤這個統計數字。當然,你可以下載這個樣本web應用程式。它需要你安裝J2SE 5.0 SDK並且你的JAVA_HOME環境變數指向基安裝目錄。J2SE 5.0實現了1.2版本的JMX API和JMX 1.0版本的Remote API。同時還需要一個支援servlet的容器;我使用的是Apache Tomcat 5.5.12。另外,我還使用Apache Ant來構建這一應用程式範例。

   二、 建立應用程式範例

  首先,你要下載應用程式範例並且使用ant war(更多的細節見build.xml中的注釋)來建立一個WAR檔案。把jmxapp.war複製到Tomcat的webapps目錄。假定Tomcat正在運行於你的本地機器的連接埠8080,那麼該應用程式的URL將是:

http://localhost:8080/jmxapp

  如果你看到一個提示你輸入名字和口令的登陸螢幕,那麼一切已經就緒了。

   三、 跟蹤一些有意義的資料

  本文中的應用程式使用Struts架構來提交登入表單。一旦提交結束,即執行LoginAction.execute(..)方法-它將簡單地檢查是否使用者的ID為"hello"以及是否其口令為"world"。如果二者都正確,那麼登入成功並且控制被導向login_success.jsp;如果不正確,那麼我們返回到登入表單。根據登入成功與否決定調用incrementSuccessLogins(HttpServletRequest)方法還是incrementFailedLogins(HttpServletRequest)方法。現在,讓我們先分析一下incrementFailedLogins(HttpServletRequest):

private void incrementFailedLogins(HttpServletRequest request) {
 HttpSession session = request.getSession();
 ServletContext context =session.getServletContext();
 Integer num = (Integer) context.getAttribute( Constants.FAILED_LOGINS_KEY);
 int newValue = 1;
 if (num != null) { newValue = num.intValue() + 1; }
 context.setAttribute( Constants.FAILED_LOGINS_KEY, new Integer(newValue));
}
  這個方法增加一個在應用程式範圍儲存的FAILED_LOGINS_KEY變數。這個incrementSuccessLogins(HttpServletRequest)方法是以相似的方法實現的。該應用程式追蹤有多少人成功地登入和有多少人認證失敗。這真不錯,但是我們該如何存取這些資料?這就是引入JMX的原因。

   四、 建立JMX MBeans

  MBeans基礎知識及其適於JMX架構的方面超出了本文所討論的範圍。我們將為我們的應用程式簡單地建立、實現、暴露和保護一個MBean。我們所感興趣的是暴露相應與下列兩個方法的兩種資料。下面是我們的簡單MBean介面:

public interface LoginStatsMBean {
 public int getFailedLogins();
 public int getSuccessLogins();
}
  這兩個方法簡單地返回成功和失敗登陸的數目。LoginStatsMBean的實現-LoginStats,為上面兩種方法提供了一種具體的實現。讓我們分析一下getFailedLogins()實現:

public int getFailedLogins() {
 ServletContext context = Config.getServletContext();
 Integer val = (Integer) context.getAttribute( Constants.FAILED_LOGINS_KEY);
 return (val == null) ? 0 : val.intValue();
}
  該方法返回一個儲存在ServletContext中的值。getSuccessLogins()方法是以相似的方式實現的。

   五、 建立和保護一個JMX代理

  管理應用程式的JMX相關方面的JMXAgent類有以下幾個責任:

  1. 建立一個MBeanServer。

  2. 用MBeanServer註冊LoginStatsMBean。

  3. 建立一個JMXConnector以允許遠程客戶進行串連。

   o 包含對JNDI的使用。

   o 也必須有一個RMI註冊運行。

  4. 使用一個使用者名稱和口令保護JMXConnector。

  5. 分別在應用程式啟動和停止時,啟動和停止JMXConnector。

  JMXAgent的類輪廓是:

public class JMXAgent {
public JMXAgent() {
//初始化JMX伺服器
}
public void start() {
//啟動JMX伺服器
}
//在應用程式結束時調用
public void stop() {
//停止JMX伺服器
}
}
  讓我們理解在該構造器的這部分代碼-它能夠使得客戶遠程地監控該應用程式。

   用MBeans建立一個MBeanServer

  我們首先建立一個MBeanServer對象。它是JMX基礎結構的核心組件,它允許我們暴露我們的MBeans作為可管理的對象。MBeanServerFactory.createMBeanServer(String)方法使得這一任務極為輕鬆。所提供的參數是伺服器的域。可以把它當作這個MBeanServer的唯一的名字。然後,我們用MbeanServe來註冊LoginStatsMBean。MBeanServer.registerMBean(Object,ObjectName)方法使用的參數有兩個:一個是MBean實現的一個執行個體;另一個是類型ObjectName的一個對象-它用於唯一地標識該MBean;在這種情況下,DOMAIN+":name=LoginStats"就足夠了。

MBeanServer server = MBeanServerFactory.createMBeanServer(DOMAIN);
server.registerMBean(new LoginStats(),new ObjectName(DOMAIN+ ":name=LoginStats"));   六、 建立JMXServiceURL

  到現在為止,我們已經建立了一個MBeanServer並且用它註冊了LoginStatsMBean。下一步是使得該伺服器對客戶可用。為此,我們必須建立一個JMXServiceURL-它描述了客戶將用來存取該JMX服務的URL:

JMXServiceURL url = new JMXServiceURL("rmi",null,
Constants.MBEAN_SERVER_PORT,
"/jndi/rmi://localhost:" +Constants.RMI_REGISTRY_PORT +"/jmxapp");
  讓我們細緻地分析一下上面一行代碼。該JMXServiceURL構造器使用了四個參數:

  1. 在串連時使用的協議(rmi,jmxmp,iiop,等等)。

  2. JMX服務的主機。用localhost作為參數就足夠了。然而,提供null強制JMXServiceURL找到可能是最好的主機名稱。例如,在這種情況下,它將把null翻譯成zarar-這是我的電腦的名字。

  3. JMX服務使用的連接埠。

  4. 最後,我們必須提供URL路徑-它指示怎樣找到JMX服務。在這種情況下,它會是/jndi/rmi://localhost:1099/jmxapp。

  其中,/jndi部分是指,客戶必須為JMX服務做一下JNDI查詢。rmi://localhost:1099指示,存在一個運行於原生連接埠1099的RMI註冊。這裡的jmxapp是在RMI註冊中唯一標識這個JMX服務的。在JMXServiceURL對象上的一個toString()產生下列結果:

service:jmx:rmi://zarar:9589/jndi/rmi://localhost:1100/jmxapp

  上面是客戶將最終使用來串連到該JMX服務的URL。J2SE 5.0文檔有關於這個URL結構的更為詳細的解釋。

  (一) 保護服務

  J2SE 5.0提供了一種有利於JMX用一種容易的方式進行使用者認證的機制。我建立了一個簡單的文字檔-它儲存使用者名稱和口令資訊。檔案的內容是:

zarar siddiqi
fyodor dostoevsky

  使用者zarar和fyodor被分別通過口令siddiqi和dostoevsky認證。下一步是建立並保護一個JMXConnectorServer,它暴露了該MbeanServer。username/password檔案的路徑被儲存在該鍵下的一個映射中-jmx.remote.x.password.file。這個映射在以後建立JMXConnectorServer時使用。

ServletContext context = Config.getServletContext();
//得到儲存jmx使用者資訊的檔案
String userFile =context.getRealPath("/")+"/WEB-INF/classes/"+Constants.JMX_USERS_FILE;
//建立authenticator並且初始化RMI伺服器
Map<string> env = new HashMap<string>();
env.put("jmx.remote.x.password.file", userFile);
現在,讓我們建立JMXConnectorServer。下面一行程式碼完成這一功能:
connectorServer = JMXConnectorServerFactory.
newJMXConnectorServer(url, env, server);


  這個JMXConnectorServerFactory.newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)方法使用我們剛建立的三個對象作為參數-它們是JMXServiceURL,儲存認證資訊的映射和MBeanServer。其中,connectorServer執行個體變數允許我們分別在應用程式啟動和停止時,分別用start()和stop()來啟動和停止JMXConnectorServer。

  提示 儘管JSR 160的J2SE 5.0實現相當有力;但是另外的實現,例如MX4J,也提供了一些類-它們提供了方便的特性,例如口令混淆,也就是PasswordAuthenticator類。

  七、 啟動RMI註冊

  在早些時候,我提到RMI註冊並且指出當訪問服務時執行一個JNDI查詢。然而,現在我們沒有一個正啟動並執行RMI註冊,因此一個JNDI查詢將失敗。一個RMI註冊的啟動可以用手工方式或編程方式來實現。

  (一) 使用命令列

  在你的Windows或Linux命令列上,輸入下列一名來啟動一個RMI註冊:

rmiregistry &

  這將啟動你的預設主控件和連接埠(分別是localhost和1109)的RMI註冊。然而,對於我們的web應用程式來說,我們不可能依賴一個在應用程式啟動時可用的RMI而寧願用編程方式來實現之。

  (二) 以編程方式啟動RMI註冊

  為了以編程方式啟動RMI註冊,你可以使用LocateRegistry.createRegistry(int port)方法。該方法傳回型別註冊的一個對象。當我們想在應用程式一端終止這個註冊時,我們儲存這個參考。就在我們啟動我們的在JMXAgent.start()中的JMXConnectorServer之前,我們首先啟動RMI註冊,使用下列程式碼:

registry = LocateRegistry.createRegistry(Constants.RMI_REGISTRY_PORT);

  在應用程式一端,在JMXAgent.stop()中停止JMXConnectorServer之後,調用下列方法來終止該註冊:

UnicastRemoteObject.unexportObject(registry,true);

  注意,StartupListener類觸發了應用程式開始和結束任務。

  八、 訪問我們的JMX服務

  我們可以有好幾種方法來存取JSR 160服務。為此,我們可以通過編程或通過使用一個GUI來實現。

  (一) 使用MC4J串連

  通過把jmxapp.war複製到Tomcat的webapps目錄來發布該應用程式。下載並且安裝MC4J。一旦安裝完,建立一新的類型JSR 160的伺服器串連並且指定該伺服器URL-它在應用程式啟動時在應用程式伺服器日誌中列印。在我的樣本中,它是:

service:jmx:rmi://zarar:9589/jndi/rmi://localhost:1100/jmxapp

  提供使用者名稱和口令,MC4J分別把它們參考為"Principle"和"Credentials"。點擊Next將把你帶到一個螢幕-在此你可以定製你的classpath。預設設定應該工作正常,並且你可以點擊"Finish"來串連到該JMX服務。一旦建立串連,瀏覽如圖1所示的MC4J樹結構,直到你找到LoginStats MBean實現的"Properties"選項。


圖1.MC4J視圖
  點擊Properties顯示統計,如圖2所示:


圖2.屬性視窗
  (二) 使用jManage串連到一個"簇"

  通過把jmxapp.war複製到Tomcat的webapps目錄發布該應用程式。請注意一下在應用程式啟動時所列印的URL。接下來,發布這個應用程式的另一個執行個體-通過改變Constants類中的RMI_REGISTRY_PORT並且MBEAN_SERVER_PORT變數,這樣該應用程式的第二個執行個體就不會試圖使用已經在使用的連接埠了。改變在build.xml檔案中的app.name屬性,以便新的執行個體將被發布到一個不同的上下文(例如,jmxapp2)。用ant建立一個清理的war檔案-它將在其目錄下建立jmxapp2.war。把jmxapp2.war複製到Tomcat的webapps目錄。該應用程式將要發布,而且現在你有相同應用程式的兩個執行個體在運行了。我再次提醒你注意在啟動時所列印的URL。

  下載和安裝jManage。一旦安裝了,使用jManage的web介面來建立一個JSR 160應用程式-通過使用首頁中的"添加新應用程式"連結。"添加應用程式"頁面顯示在圖3中:


圖3."添加應用程式"頁面
  為要發布的第二個應用程式重複前面的步驟並再次使用適當的使用者名稱、口令和URL。。一旦你建立了這兩個應用程式,你必須通過遵循在首頁中找到的"添加新應用程式簇"連結來建立一個簇。現在,把這兩個已經建立的應用程式添加到你的簇上,如圖4所示:


圖4.添加應用程式簇頁面

  好了,我們已經完成了!從首頁上,點擊簇中的一個應用程式,然後點擊"Find More Objects"按鈕。你將看到name=LoginStats MBean;點擊它,則你就會看到我們已經暴露的FailedLogins和SuccessLogins屬性。點擊在該同一頁面上的"Cluster View"連結將顯示與圖5相類似的一個頁面-其中,你可以看到兩個應用程式的運行計數統計:


圖5.針對jmxapp和jmxapp2的簇視圖
  試著登入到兩個應用程式(http://localhost:8080/jmxapp和http://localhost:8080/jmxapp2)並且觀察這些數字是怎樣改變的。

  九、 結論

  現在你已經知道了怎樣使你的新的和現有web應用程式支援JMX並且安全地管理它們-使用MC4J和jManage。儘管J2SE 5.0提供了JMX說明書的一個有力的實現,但是另外的開源工程例如XMOJO和MX4J還提供了另外的特徵,例如經由web介面甚至更多的方式的串連。如果有興趣的讀者想瞭解更多地有關JMX的知識,你可以看一下J. Steven Perry寫的《Java Management Extensions》一書。如果你對遠程應用程式管理感興趣的話,Jeff Hanson寫的《Connecting JMX客戶and Servers》將是很有閱讀價值的,其中提供了許多真實世界的例子。

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。