1. 程式人生 > >Tomcat容器管理安全的幾種驗證方式

Tomcat容器管理安全的幾種驗證方式

https://my.oschina.net/itblog/blog/678845?fromerr=mnnjJA0O

摘要: 本文介紹如何使用容器(這裡指tomcat)來進行安全管理。

當訪問伺服器中受保護的資源時,容器管理的驗證方法可以控制確認使用者身份的方式。Tomcat支援四種容器管理的安全防護,它們是:

  • BASIC(基本驗證):通過HTTP驗證,需要提供base64編碼文字的使用者口令
  • DIGEST(摘要驗證):通過HTTP驗證,需要提供摘要編碼字串的使用者口令
  • FORM(表單驗證):在網頁的表單上要求提供密碼
  • CLIENT-CERT(客戶端證書驗證):以客戶端證書來確認使用者的身份

 基本驗證

當web.xml檔案中的auth-method元素設定為BASIC時,表明應用使用的是基本驗證,每次瀏覽器請求受保護的Web應用資源時,Tomcat都會使用HTTP基本驗證向瀏覽器索取使用者名稱和密碼(以頁面彈窗的方式)。使用這種驗證方法,所有的密碼都會以base64編碼的文字在網路上傳輸。

先看下專案結構(我用Maven管理的依賴):

其中,protect/protect.jsp是被保護的,需要授權訪問。

說明:本文提到的tomcat-users.xml,server.xml等檔案,如果是在Eclipse中啟動tomcat,則這些檔案在Eclipse中的Servers工程下對應的tomcat下,如圖:

本文提到的web.xml是指專案自己的web.xml,而非Servers專案下Tomcat中的web.xml

web.xml

<security-constraint>
	<web-resource-collection>
		<http-method>GET</http-method>
		<web-resource-name>tomcat protect page</web-resource-name>
		<!-- /protect目錄下的所有資源是受保護的 -->
		<url-pattern>
/protect/*</url-pattern> </web-resource-collection> <auth-constraint> <!-- 這裡的member要與tomcat-user.xml中配置的role一致 --> <role-name>member</role-name> </auth-constraint> </security-constraint> <login-config> <!-- 驗證方式,可選的值為: "BASIC", "DIGEST", "FORM", "CLIENT-CERT" --> <auth-method>BASIC</auth-method> <!-- 使用的Realm名字,注意這裡不能有空格 --> <realm-name>MyConstraints</realm-name> </login-config>

tomcat-user.xml(注意如果是在Eclipse中啟動tomcat,這個tomcat-user.xml在Eclipse中的Servers工程下)

<rolerolename="member"/>
<!-- member角色下有一個叫alvis的使用者,密碼為pwd -->
<userusername="alvis"password="pwd"roles="member"/>

重啟tomcat後,訪問protect目錄下的資源,情況是這樣的:

輸入賬戶alvis,密碼pwd後,訪問成功(當然,非protect目錄下的資源是可以直接訪問的):

摘要驗證

當web.xml檔案中的auth-method元素設定為DIGEST時,表明應用使用的是摘要驗證。還是上面的例子,看配置:

web.xml和基本驗證一樣,只是auth-method修改為DIGEST,此處不贅述。

server.xml中的UserDatabaseRealm(如果tomcat使用的是其他Realm,也一樣的)裡增加digest屬性:

接下來,要生成tomcat可識別的MD5密碼。方式有兩種,正如官網描述:

To calculate the digested value of a cleartext password, two convenience techniques are supported:

  • If you are writing an application that needs to calculate digested passwords dynamically, call the static Digest()method of the org.apache.catalina.realm.RealmBase class, passing the cleartext password and the digest algorithm name as arguments. This method will return the digested password.
  • If you want to execute a command line utility to calculate the digested password, simply execute
    CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}
    
    and the digested version of this cleartext password will be returned to standard output.

方式一:用程式碼來生成:

import org.apache.catalina.realm.RealmBase;

public class T {
	publicstaticvoidmain(String[] args) {
		//引數1:要加密的字串;引數2:加密演算法;引數3:字串的編碼
		String base = RealmBase.Digest("alvis:MyConstraints:pwd", "MD5", null);
		System.out.println(base);
	}
}

由於RealmBase類在catalina.jar包中,如果專案中沒有這個類,可在專案上右鍵-->Java Build Path--> Libraries-->Add Library-->選擇Server Runtime-->選擇Apache Tomcat V8.0(其實7.0也行),如圖:

方式二:用指令碼來生成:

在tomcat/bin目錄下有個digest.sh(Linux系統)或digest.bat(Windows系統)指令碼,執行這個指令碼,傳入摘要演算法和引數即可,這裡我在Windows系統上執行,如圖:

這裡的-a指定摘要演算法為MD5,要特別注意這裡的引數是:{使用者名稱}:{Realm名}:{密碼明文}。使用者名稱就是tomcat-users.xml中配置的<user>名字(這裡為alvis),Realm名是在web.xml中配置的<realm-name>(這裡為MyConstraints),密碼明文即該使用者用於登入的密碼(我這裡設為pwd)。

只有這樣的引數加密後的密碼,在tomcat-users.xml中配置才有效,否則是登入不了的。由於我是參考《Tomcat權威指南(第二版)》的步驟做的,之前試了很久都不知道為什麼登入不了,結果在官網找到答案,是這麼描述的:

If using digested passwords with DIGEST authentication, the cleartext used to generate the digest is different and the digest must use the MD5 algorithm. In the examples above {cleartext-password} must be replaced with {username}:{realm}:{cleartext-password}. For example, in a development environment this might take the form testUser:Authentication required:testPassword. The value for {realm} is taken from the <realm-name> element of the web application's <login-config>. If not specified in web.xml, the default value of Authentication required is used.

大意是說,如果使用DIGEST方式驗證,用於生成摘要的明文必須被替換為這種格式。實踐出真知,所以還是不能完全看書啊,動手實踐才是實在的。

然後就是在tomcat-users.xml中配置生成的密碼(通過下方的截圖,可以比較password跟上方digest.bat指令碼生成的密碼是否一致):

之後重啟tomcat,效果自然是跟使用基本驗證的效果一樣了。

表單驗證

當web.xml檔案中的auth-method元素設定為FORM時,表明應用使用的是表單驗證。當用戶請求Web應用程式受保護的資源時,表單驗證會跳轉至配置的登入頁面。當登入失敗時,還需要一個驗證失敗的頁面,還是上面的例子,看配置:

web.xml

<security-constraint>
	<web-resource-collection>
		<http-method>GET</http-method>
		<web-resource-name>tomcat member part</web-resource-name>
		<url-pattern>/protect/*</url-pattern>
	</web-resource-collection>
	<auth-constraint>
		<role-name>member</role-name>
	</auth-constraint>
</security-constraint>
<login-config>
	<auth-method>FORM</auth-method>
	<realm-name>MyConstraints</realm-name>
	<form-login-config>
		<form-login-page>/form/login.html</form-login-page>
		<form-error-page>/form/error.html</form-error-page>
	</form-login-config>
</login-config>

這裡的form/login.html是用於登入的頁面,而form/error.html則是驗證失敗後跳轉到的頁面(這兩個頁面在上方的工程結構圖中已經有了)。

login.html

<html>
	<body>
	<h2>Login Page.</h2>
	
	<formmethod="post"action="j_security_check"name="loginForm">
		<inputtype="text"name="j_username" /><br>
		<inputtype="password"name="j_password" /><br>
		<inputtype="submit"value="Login" />
	</form>
	
	</body>
</html>

注意:這裡form的action="j_security_check",賬號的name="j_username"和密碼的name="j_password"都是不可變的,否則配置的驗證規則不起作用

server.xml中,要去掉Realm中新增的“digest=MD5”這個屬性:

tomcat-users.xml中使用明文儲存密碼:

效果(僅在訪問protect目錄下的資源時才出現Login Page):

輸入錯誤的賬號和密碼,跳轉至form/error.html頁面:

輸入正確的賬號和密碼,跳轉至受保護的頁面:

客戶端證書驗證

待續

Demo下載:

參考頁面:

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

HTTP 驗證

HTTP 協議提供驗證機制來保護資源。當一個請求要求取得受保護的資源時,網頁伺服器迴應一個 401 Unauthorized error 錯誤碼。這個迴應包含一個指定了驗證方法和領域的 WWW-Authenticate 頭資訊。把這個領域想像成一個儲存著使用者名稱和密碼的資料庫,它將被用來標識受保護資源的有效的使用者。比如,你試著去訪問某個網站上標識為“Personal Files”的資源,伺服器響應可能是:WWW-Authenticate: Basic realm="Personal Files" (假設驗證方法是 Basic)。

驗證方法

現在有幾種用於網路應用的驗證方法,其中最廣泛使用的是基本驗證 (Basic Authentication) 和摘要驗證 (Digest Authentication)。

當用戶想要訪問有限的資源時,使用基本驗證方法的網頁伺服器會要求瀏覽器詢顯示一個對話方塊,並要求使用者輸入使用者名稱和密碼。如果使用者輸入的使用者名稱和密碼正確,伺服器就允許他訪問這些資源;否則,在接連三次嘗試失敗之後,會顯示一個錯誤訊息頁面。這個方法的缺點是使用者名稱和密碼都是用 Base64 編碼 (全是可讀文字) 之後傳輸過去的。也就是說,這個驗證方法的安全程度只是和 Telnet 一樣,並不是非常安全。

資料驗證方法不會在網路中傳輸密碼,而是生成一些數字 (根據密碼和其它一些需要的資料產生的) 來代替密碼,而這些數字是經過 MD5 (Message Digest Algorithm) 加密的。生成的值在網路有隨著伺服器需要用來校難密碼的其它資訊一起傳輸。這個方法明顯更為安全。

基於表單的驗證方法和基本驗證方法類似,只是伺服器使用你自定義的登入頁面來代替了標準的登入對話方塊。

最後,客戶證書驗證使用 SLL (Secure Socket Layer,安全套接層) 和客戶證明。

在 Tomcat 下保護資源

你可以在 tomcat-users.xml 檔案中寫一個使用者及其角色的列表。這個檔案在 TOMCAT_HOME (你安裝 Tomcat 的目錄) 下的 conf 目錄中。這個檔案預設包含了三個使用者 (tomcat、role1、both) 的定義。下面一段 XML 程式碼是我添加了兩個新使用者 (qusay 和 reader) 之後的 tomcat-users.xml:


<tomcat-users>
<user name="tomcat" password="tomcat" roles="tomcat" />
<user name="role1" password="tomcat" roles="role1" />
<user name="both" password= "tomcat" roles="tomcat,role1" />
<user name="qusay" password="guesswhat" roles="author" />
<user name="reader" password="youguess" roles="reader" />
</tomcat-users>



新新增的兩個使用者 (qusay 和 reader) 的 roles 分別設定為 author 和 reader。角色屬性非常重要,因為當你建立安全規則的時候,每個受限制的資源都是與可訪問它的角色相關聯 (稍後你會看到)。

下面做個實驗 (假設你已經安裝並配置好了 Tomcat)。為你期望的頁應用程式建立一個目錄。可以按下列步驟做好準備:

1. 在你安裝了 Tomcat 的目錄下,有一個目錄是 webapps。在這個目錄下建立一個目錄 (如:learn)。 
2. 在第一步建立的目錄下建立一個子目錄,命名為 chapter。 
3. 在 chapter 目錄中,建立一個 HTML 檔案,內容自定,檔名為 index.html。 
4. 在第一步建立的目錄下建立一個名為 WEB-INF 的子目錄。 
5. 在 WEB-INF 目錄中建立一個名為 web.xml 的檔案,該檔案內容如下: 



6. <?xml version="1.0" encoding="ISO-8859-1"?>
7. 
8. <!DOCTYPE web-app
9. PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
10. "http://java.sun.com/dtd/web-app_2_3.dtd">
11. 
12. <web-app>
13. 
14. <description>
15. Learning Web Programming
16. </description>
17. 
18. <security-constraint>
19. <web-resource-collection>
20. <web-resource-name>
21. Restricted Area
22. </web-resource-name>
23. <url-pattern>/chapter/*</url-pattern>
24. </web-resource-collection>
25. <auth-constraint>
26. <role-name>tomcat</role-name>
27. <role-name>author</role-name>
28. <role-name>reader</role-name>
29. </auth-constraint>
30. </security-constraint>
31. 
32. <login-config>
33. <auth-method>BASIC</auth-method>
34. <realm-name>Authenticate yourself</realm-name>
35. </login-config>
36. 
</web-app>




web.xml 配置描述

web.xml 是描述配置的檔案,這裡集中說明一下安全相關的配置元素。

<security-constraint>:這個元素限制對一個或者多個資源的訪問,可以在配置資訊中出現多次。上面的配置資訊中,它限制了 chapter 目錄 (http://localhost:8080/learn/chapter) 下所有資源的訪問。<security-constraint> 包含了下列元素:
o <web-resource-collection>:這個元素用於標識你想限制訪問的資源。你可以定義 URL 模式 和 HTTP 方法 (用 <http-method> 元素定義 HTTP 方法)。如果沒有定義 HTTP 方法,那麼限制將應用於所有方法。在上面的應用中,我想限制訪問的資源是 http://localhost:8080/learn/chapter/*,也就是 chapter 目錄下的所有文件。
o <auth-constraint>:這個元素可以訪問上面定義的受限資源的使用者角色。在上面的應用中,tomcat、author 和 erader 這三個角色可以訪問這些資源。 

<login-config>:這個元素用於指定驗證方法。它包含下列元素:
o <auth-method>:指定驗證方法。它的值可能是下列值集中的一個:BASIC (基本驗證)、DIGEST (摘要驗證)、FORM (基於表單的驗證) 或者 CLIENT-CERT (客戶證書驗證)。
o <realm-name>:如果選用 BASIC 方法進行驗證的時候,標準登入對話方塊中的一個描述名稱。 

示例

上述配置中使用了 BASIC 驗證方法。下面我們做個實驗:啟動你的 Tomcat 伺服器並向它傳送到 http://localhost:8080/learn/chapter 的請求。這時候,就會有像圖 3 所示那樣的對話方塊提示你輸入使用者和密碼:


圖 3:HTTP 基本驗證 (Basic Authentication)



輸入一個使用者及其密碼 (你可以看看 tomcat-users.xml 檔案),這個使用者的角色應該在配置 (web.xml) 中存在。如果你輸入的使用者名稱和密碼正確,你就能訪問到那些資源;否則,你還可以再試兩次。

使用摘要驗證來實驗:

· 關閉你的 Tomcat 伺服器。 
· 修改你的配置檔案 (web.xml),把 BASIC 換成 DIGEST。 
· 重新啟動你的 Tomcat 伺服器。 
· 開啟一個新的瀏覽器視窗。 
· 在位址列中輸入 http://localhost:8080/learn/chapter 並回車。 

你會看到類似的對話方塊。從圖 4 你可以看到,這個登入對話方塊是安全的,因為使用了摘要驗證。



圖 4:HTTP 摘要驗證 (Digest Authentication)



伺服器在幕後的回覆

當使用基本驗證方法保護資源的時候,伺服器發回類似於圖 5 所示的響應資訊:


圖 5:伺服器回覆 (基本驗證)



如果是使用的摘要驗證方法來保護的資源,伺服器發回的響應資訊就像圖 6 所示的那樣:



圖 6:伺服器回覆 (摘要驗證)



Java 支援 HTTP 驗證

J2SE (1.2 或者更高版本) 通過 Authenticator 類為驗證提供了本地支援。你所要做的只是繼承這個類並實現它的 getPasswordAuthentication 方法。這個方法取得使用者名稱和密碼並用它們生成一個 PasswordAuthentication 物件返回。完成之後,你還得使用 Authenticator.setDefault 方法註冊你的 Authenticator 例項。現在,只要你想訪問受保護的資源,就會呼叫 getPasswordAuthentication。Authenticator 類管理著所有低層的詳細資料。它不受 HTTP 的限制,可以應用於所有網路連線,這不能不說是一個好訊息。

示例程式碼 6 中是實現 Authenticator 的一個示例。正如你所看到的,在請求驗證的時候,getPasswordAuthentication 方法會彈出一個登入對話方塊。

示例程式碼 6:AuthImpl.java


import java.net.*;
import java.awt.*;
import javax.swing.*;

public class AuthImpl extends Authenticator {
protected PasswordAuthentication getPasswordAuthentication() {
JTextField username = new JTextField();
JTextField password = new JPasswordField();
JPanel panel = new JPanel(new GridLayout(2,2));
panel.add(new JLabel("User Name"));
panel.add(username);
panel.add(new JLabel("Password") );
panel.add(password);
int option= JOptionPane.showConfirmDialog(null,
new Object[] {"Site: "+getRequestingHost(),
"Realm: "+getRequestingPrompt(),panel},
"Enter Network Password",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE);
if ( option == JOptionPane.OK_OPTION ) {
String user = username.getText();
char pass[] = password.getText().toCharArray();
return new PasswordAuthentication(user, pass);
} else {
return null;
}
}
}



示例程式碼 7 用來做測試的程式碼。我做的第一件事情就是用 Authenticator.setDefault 讓我的 Authenticator 例項開始執行。我不必去解析任何伺服器返回的資訊以檢查是否需要驗證,因為 Authenticator 類非常聰明,它知道是否需要驗證。

示例程式碼 7:BasicReader.java


import java.io.*;
import java.net.*;

public class BasicReader {
public static void main(String argv[]) throws Exception {
Authenticator.setDefault(new AuthImpl());
if (argv.length != 1) {
System.err.println("Usage: java BasicReader <site>");
System.exit(1);
}
URL url = new URL(argv[0]);
URLConnection connection = url.openConnection();
BufferedReader in= new BufferedReader(new InputStreamReader(connection.getInputStream()));

String line;
StringBuffer sb = new StringBuffer();
while ((line = in.readLine()) != null) {
sb.append(line);
}
in.close();
System.out.println(sb.toString());
System.exit(0);
}
}



執行下面的命令,用 Tomcat 來進行測試:

prompt> java BasicReader http://localhost:8080/learn/chapter

如果你進入的站點需要驗證 (它也這樣做了),那會像圖 7 那樣的對話方塊就會顯示出來。


圖 7:Java 處理 HTTP 驗證



一旦你輸入了正確的使用者名稱和密碼,伺服器就會允許你訪問。BasicReader 會讀取被請求頁面的 HTML 內容並顯示在你的控制檯視窗。

特別注意:在 Tomcat 4.0 中使用摘要驗證時你可能會遇到問題,這是由 J2SE 1.4.0 和 J2SE 1.4.1 的一個 BUG 引起的。不過這個問題已經在 J2SE 1.4.2 中解決了。詳情情看這裡。 

總結

這篇文章是一篇教程,它介紹了 java.net 包的高層 API。這些 API 使你可以快速簡捷地建立有用的網路應用程式,如 StockReader。這裡也討論了 HTTP 驗證,並用例項演示瞭如何使用你自己的驗證方案。URL 和 URLConnection 還有一些優勢沒有在文中提到,它們包括:自動重定向、自動管理保持的連線等。不過現在你已經從文中獲得基礎知識,可以自己解決這些問題了。