http://www-128.ibm.com/developerworks/cn/java/l-j2eesecurity2/index.html系統(tǒng)架構(gòu)工程師、系統(tǒng)分析員
2002 年 9 月 02 日
在本系列文章的第一部分作者介紹了j2ee的安全概念、驗(yàn)證模型和授權(quán)模型,這部分更偏重于理論的介紹。本文的第二部分作者將通過具體的例子向讀者展示如何在開發(fā)中應(yīng)用j2ee提供的安全服務(wù)。本部分的重點(diǎn)在于應(yīng)用與實(shí)踐。
注釋:本文的目的是介紹如何應(yīng)用j2ee提供的安全服務(wù),而并不針對(duì)特定的產(chǎn)品。因此作者選擇sun的 j2ee參考實(shí)現(xiàn)(j2sdkee)作為演示平臺(tái)。因?yàn)閖2sdkee是完全遵照j2ee規(guī)范開發(fā)的,雖然它不像IBM WebSphere 、BEA WebLogic等j2ee產(chǎn)品那么產(chǎn)品化和商業(yè)化,但它絕對(duì)是學(xué)習(xí)j2ee的理想平臺(tái)。你可以通過 http://java.sun.com/j2ee/獲取sun 的j2ee參考實(shí)現(xiàn)的最新版本。本文選擇的是Sun的j2sdkee1.3.1。
本文將包括以下內(nèi)容:
- 一個(gè)采用HTTP基本的驗(yàn)證的例子
- 一個(gè)采用基于表單的驗(yàn)證的例子
- 一個(gè)ejb方法授權(quán)的例子
- 一個(gè)可編程安全性和傳播調(diào)用者身份標(biāo)識(shí)的例子
采用HTTP基本的驗(yàn)證的例子
http基本驗(yàn)證是Web客戶端驗(yàn)證的一種,它和系統(tǒng)的授權(quán)機(jī)制一起控制受保護(hù)資源的訪問。
步驟:
1. 創(chuàng)建一個(gè)j2ee應(yīng)用
在應(yīng)用程序部署工具的File菜單選中New子菜單中的Application菜單項(xiàng)(見圖1)。會(huì)彈出新建應(yīng)用程序?qū)υ捒颉L顚憫?yīng)用程序文件名和應(yīng)用程序顯示名(見圖2)。
圖1
圖2
2. 創(chuàng)建一個(gè)web組件
在應(yīng)用程序部署工具的File菜單選中New子菜單中的Web Compent菜單項(xiàng),會(huì)彈出新建web組件向?qū)?duì)話框(見圖3)。選擇Create New WAR File in Application,在下拉框中選擇步驟1創(chuàng)建的應(yīng)用test,在WAR Display Name框中填寫WebAppTest.點(diǎn)擊Content欄中的Eidt按鈕選擇此Web組件包含的文件。在這個(gè)例子中只有一個(gè)webtest.jsp文件。然后點(diǎn)擊Next,進(jìn)入下一個(gè)對(duì)話框(見圖4)。由于我們的web組件是一個(gè)jsp文件,因此在組件類型中選擇JSP。然后一直按Next直到結(jié)束。此時(shí)我們已經(jīng)創(chuàng)建了一個(gè)只包含一個(gè)jsp文件的web組件。接下來是配置安全屬性的步驟。
圖3
圖4
3. 配置安全屬性
3.1創(chuàng)建安全角色
在部署工具的左導(dǎo)航欄中點(diǎn)中步驟2創(chuàng)建的web組件WebAppTest,在右邊的屬性頁中選擇Roles屬性頁(見圖5)。點(diǎn)擊Add按鈕,在Name欄中填寫安全角色名user,Description欄填寫描述信息。安全角色代表具有相同安全權(quán)限用戶的集合。
圖5
3.2 配置安全策略
創(chuàng)建了安全角色后,應(yīng)該對(duì)安全角色配置相應(yīng)的安全策略。點(diǎn)擊Security屬性頁(見圖6)。
圖6
首先選擇你想用的驗(yàn)證方式,從User Authentication Method下拉框中選擇Basic。這意味著你將通過基本的HTTP驗(yàn)證方式驗(yàn)證用戶。下面我們進(jìn)行web資源的授權(quán)。點(diǎn)擊Security Constraint欄中的Add按鈕添加一條安全約束,約束名可以自定。接下來對(duì)創(chuàng)建好的約束添加Web資源。首先在Web Resource Collections中添加資源集合,然后選取資源集合包含的資源。此例中WRCollection資源集合中包含webtest.jsp文件,也可以包含各種屬于這個(gè)web組件的文件。接下來選擇哪些web操作要收到約束,j2sdkee1.3.1中只包含兩種操作(GET和POST),不同的產(chǎn)品支持的操作有所不同,在開發(fā)是應(yīng)結(jié)合具體產(chǎn)品提供的操作來選取。現(xiàn)在應(yīng)該指定安全角色了,點(diǎn)擊Authorized Roles欄中的Edit按鈕,會(huì)彈出安全角色列表對(duì)話框,從中選取已定義的安全角色。本例中選擇user。至此安全策略已經(jīng)配置完畢,下面的步驟是將實(shí)際環(huán)境中的用戶和用戶組映射與安全角色進(jìn)行映射。
4. 映射
在左導(dǎo)航欄中選中應(yīng)用程序test在右邊選擇Security屬性頁(見圖7),在Role Name Reference欄中選中user,點(diǎn)擊正下方的Add按鈕,會(huì)彈出用戶和用戶組列表對(duì)話框,從中選擇要映射成安全角色user的用戶或組。此例中我們將用戶j2ee映射為安全角色user。這樣用戶J2ee將具有為安全角色user分配的訪問授權(quán)。
圖7
5. 部署應(yīng)用
選中Web Context屬性頁,在Context Root文本框中填寫test,右鍵點(diǎn)擊左導(dǎo)航欄的應(yīng)用test,在彈出菜單中選擇deploy完成應(yīng)用程序的發(fā)布。至此我們完成了第一個(gè)例子的全部步驟。
部署描述文件
這個(gè)例子使用了說明性的安全服務(wù),因此我們不需要編寫任何的安全相關(guān)的代碼,而是完全通過配置組件的部署描述文件來實(shí)現(xiàn)的。下面是這個(gè)web組件的部署描述文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
'-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
'http://java.sun.com/dtd/web-app_2_3.dtd'>
<web-app>
<display-name>WebAppTest</display-name> //Web組件名稱
<servlet>
<servlet-name>webtest</servlet-name>
<display-name>webtest</display-name>
<jsp-file>/webtest.jsp</jsp-file> //組件中包含的jsp文件
</servlet>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<security-constraint> //安全約束部分
<web-resource-collection> //受約束的web資源集
<web-resource-name>WRCollection</web-resource-name> //資源集名
<url-pattern>/webtest.jsp</url-pattern> //資源的url表達(dá)式
<http-method>GET</http-method> //受約束的資源操作方法
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint> //對(duì)安全角色授權(quán)
<role-name>user</role-name> //安全角色名
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config> //驗(yàn)證方式設(shè)置
<auth-method>BASIC</auth-method> //使用基本的HTTP驗(yàn)證方式
<realm-name></realm-name>
</login-config>
<security-role> //定義安全角色
<description>this is a user</description>
<role-name>user</role-name>
</security-role>
</web-app>
|
從部署描述文件可以知道這是一個(gè)名為WebAppTest的web組件,包含一個(gè)名為webtest.jsp的文件,只有被賦予user安全角色的用戶或用戶組才有權(quán)對(duì)webtest.jsp進(jìn)行GET或POST操作。這里并沒有包含安全角色對(duì)實(shí)際用戶的映射,j2ee部署描述文件的DTD中并沒有定義用于安全角色和實(shí)際用戶的映射的元素,因?yàn)閷?shí)際環(huán)境中有多種不同的用戶系統(tǒng)(如關(guān)系數(shù)據(jù)庫,系統(tǒng)文件形式和LDAP系統(tǒng)等)。因此安全角色和實(shí)際用戶的映射方式是由j2ee產(chǎn)品廠商制定的。
測(cè)試運(yùn)行結(jié)果
打開ie,在導(dǎo)航欄輸入http://localhost:8000/test/webtest.jsp回車,會(huì)彈出驗(yàn)證對(duì)話框,要求用戶提供用戶名和密碼(見圖8),輸入用戶名j2ee和密碼j2ee。通過用戶驗(yàn)證后執(zhí)行jsp文件,webtest.jsp打印出"hello!"(見圖9)。
圖8
圖9
注釋:在第一個(gè)例子中已經(jīng)詳細(xì)的描述了各個(gè)步驟,在接下來的例子中會(huì)有一些與第一個(gè)例子相同的操作,因此對(duì)下面的例子只描述與第一個(gè)例子不同的步驟。
基于表單的驗(yàn)證的例子
基于表單的驗(yàn)證與基本的HTTP驗(yàn)證的唯一區(qū)別是基本的HTTP驗(yàn)證用瀏覽器提供的驗(yàn)證信息對(duì)話框收集用戶驗(yàn)證信息,而基于表單的驗(yàn)證允許自定義登陸頁面來收集用戶驗(yàn)證信息。本例子與第一個(gè)例子的步驟基本相同,不同的地方在于此例子要提供登陸頁面和出錯(cuò)頁面。
登陸頁面login.html
<form method="POST" action="j_security_check">
<input type=text name="j_username">
<input type=password name="j_password">
<input type=submit name="login" value="login">
</form>
|
此文件有幾個(gè)地方值得注意:
- Action的值必須為"j_security_check"
- 獲取用戶名的域名必須是"j_username"
- 獲取用戶密碼的域必須是" j_password"
出錯(cuò)頁面 error.html
<html>
用戶名或密碼不正確!
</html>
|
出錯(cuò)頁面只是簡(jiǎn)單的顯示出錯(cuò)信息。
配置基于表單的驗(yàn)證
首先將login.html和error.html加入到WebAppTest組件中。
然后見圖10選擇Security屬性頁,在User Authentication Method下拉框中選擇Form Based選項(xiàng)。點(diǎn)擊Settings…彈出用戶驗(yàn)證設(shè)置對(duì)話框,在Login Page下拉框選login.html,在Error Page下拉框選error.html。
圖10
重新部署應(yīng)用,再一次訪問http://localhost:8000/test/webtest.jsp 會(huì)出現(xiàn)login頁面(見圖11),如果用戶名或密碼錯(cuò)誤,error.html將顯示給用戶。
圖11
部署描述文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
'-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
'http://java.sun.com/dtd/web-app_2_3.dtd'>
<web-app>
<display-name>WebAppTest</display-name>
.
.
.
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method> //使用基于表單的驗(yàn)證方式
<realm-name>Default</realm-name> //使用缺省的安全域
<form-login-config>
<form-login-page>/login.html</form-login-page> //定義登陸頁面
<form-error-page>/error.html</form-error-page> //定義出錯(cuò)頁面
</form-login-config>
</login-config>
<security-role>
<description>this is a user</description>
<role-name>user</role-name>
</security-role>
</web-app>
|
ejb方法授權(quán)的例子
從j2ee1.3開始便提供了對(duì)ejb的方法進(jìn)行授權(quán)的安全服務(wù),這種授權(quán)服務(wù)由ejb容器實(shí)現(xiàn)。當(dāng)調(diào)用者調(diào)用ejb的方法時(shí),ejb容器用調(diào)用者的身份來查找授予此調(diào)用者的訪問權(quán)限條目,如果調(diào)用者調(diào)用的方法屬于授權(quán)條目,那么ejb容器調(diào)用方法。否則,ejb容器拒絕調(diào)用此方法,并向調(diào)用者返回拒絕訪問異常。可以對(duì)遠(yuǎn)程方法和home接口方法進(jìn)行授權(quán)。本例中我們將對(duì)一個(gè)遠(yuǎn)程方法和一個(gè)home接口方法進(jìn)行授權(quán)。
首先創(chuàng)建一個(gè)session bean CountEjb
遠(yuǎn)程接口 Count.java
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Count extends EJBObject {
/**
* 遠(yuǎn)程方法count
*/
public int count() throws RemoteException;
}
Home接口 CountHome.java
import javax.ejb.*;
import java.rmi.RemoteException;
/**
* This is the home interface for CountBean.
* One create() method is in this Home Interface, which
* corresponds to the ejbCreate() method in the CountBean file.
*/
public interface CountHome extends EJBHome {
/*
* This method creates the EJB Object.
*
* @param val Value to initialize counter to
*
* @return The newly created EJB Object.
*/
Count create(int val) throws RemoteException, CreateException;
}
實(shí)現(xiàn)類 CountBean.java
import javax.ejb.*;
import java.security.Principal;
/**
public class CountBean implements SessionBean {
// The current counter is our conversational state.
public int val;
private SessionContext sessionCtx;
//
// 遠(yuǎn)程商業(yè)方法實(shí)現(xiàn)
public int count() {
System.out.println("count()");
return ++val;
}
//
// home接口Create方法的實(shí)現(xiàn)
//
public void ejbCreate(int val) throws CreateException {
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbRemove() {
System.out.println("ejbRemove()");
}
public void ejbActivate() {
System.out.println("ejbActivate()");
}
public void ejbPassivate() {
System.out.println("ejbPassivate()");
}
public void setSessionContext(SessionContext ctx) {
sessionCtx=ctx;
}
}
客戶端程序 CountClient.java
import javax.ejb.*;
import javax.naming.*;
import java.util.Properties;
/**
* This class is a simple example of client code.
*/
public class CountClient {
public static void main(String[] args) {
try {
InitialContext ctx = new InitialContext();
CountHome home = (CountHome)
javax.rmi.PortableRemoteObject.narrow(
ctx.lookup("java:comp/env/CountHome"), CountHome.class);
int countVal = 0;
Count count=null;
/*
創(chuàng)建并執(zhí)行遠(yuǎn)程方法
*/
System.out.println("Instantiating beans...");
count = home.create(countVal);
countVal = count.count();
System.out.println(countVal);
/*
remove Count對(duì)象
*/
count.remove();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
|
這個(gè)ejb包括一個(gè)遠(yuǎn)程商業(yè)方法count(),我們將將此方法授權(quán)給某個(gè)安全角色。此外還將home接口的Create()方法授權(quán)給安全角色。
步驟1
編譯以上源程序并用j2sdkee1.3.1的組裝發(fā)布工具(deploytool.bat)進(jìn)行組裝(如圖12)。
圖12
步驟2:配置安全角色
在對(duì)方法進(jìn)行授權(quán)前,必須創(chuàng)建將被授權(quán)的安全角色。創(chuàng)建安全角色的步驟前邊已經(jīng)介紹過了,此處不再重復(fù)。本例中我們創(chuàng)建名為admin的安全角色。
步驟3:方法授權(quán)
方法授權(quán)的過程是確定那些安全角色可以訪問特定方法的過程。方法授權(quán)一般是應(yīng)用程序組裝或應(yīng)用程序部署者的責(zé)任。他們根據(jù)企業(yè)特定的需求創(chuàng)建不同的安全角色,并授予這些安全角色特定的訪問權(quán)限。
用鼠標(biāo)選中CountBean,在右端的窗口選擇Security屬性頁(如圖13),在Security Identity選項(xiàng)中選擇Use Caller ID,這意味著ejb容器將用方法調(diào)用者的身份來驗(yàn)證方法調(diào)用權(quán)限。 Run As Specified Role選項(xiàng)將在"傳播調(diào)用者身份標(biāo)識(shí)的例子"進(jìn)行介紹。由于在前面創(chuàng)建了admin安全角色,因此你可以看到Method Permissions欄中出現(xiàn)admin列。首先對(duì)遠(yuǎn)程方法count()進(jìn)行授權(quán)。選擇Remote選項(xiàng),并在count()方法的Availability列中選擇Sel Roles,然后選中count()方法的admin列。到此為止我們已對(duì)遠(yuǎn)程方法count()進(jìn)行了授權(quán)。接下來對(duì)home接口的create()方法進(jìn)行授權(quán)。在Show欄中選擇Remote Home,剩下的步驟與count()方法授權(quán)相同。我們已經(jīng)將count()方法和create()方法授權(quán)給了admin安全角色。但安全角色這是一個(gè)邏輯的集合,并不代表具體的用戶或用戶組,因此結(jié)下來我們要做的就是將安全角色與實(shí)際的用戶映射起來。
步驟4:角色映射
首先我們需要在我們的j2ee環(huán)境中創(chuàng)建一個(gè)用戶,用戶起名為Tony,密碼為1。
圖13
這里我們使用用戶名和密碼的方式進(jìn)行身份驗(yàn)證。我們?cè)赿efault Realm中創(chuàng)建此用戶。可以使用命令行方式:"realmtool -add Tony 1 eng"。詳細(xì)的使用方法參見j2sdk1.3.1文檔的工具部分。接下來映射安全角色到用戶。選中ejb應(yīng)用CountEjb,在右邊窗口中選擇Security屬性頁(如圖14),點(diǎn)擊Edit Roles按鈕,選擇安全角色admin。再點(diǎn)擊Add按鈕選擇Tony用戶。這樣已經(jīng)將安全角色admin和用戶Tony映射起來了。
步驟5:部署應(yīng)用
部署應(yīng)用到本地機(jī),右鍵點(diǎn)擊ejb應(yīng)用CountEjb,選擇彈出菜單的deploy項(xiàng),按要求配置各項(xiàng)。
步驟6:創(chuàng)建客戶端
創(chuàng)建客戶端將客戶端程序的主類和其他輔助類打包。創(chuàng)建端將客的過程比較簡(jiǎn)單,這里就不作描述了。
步驟7:運(yùn)行程序
現(xiàn)在可以運(yùn)行客戶端程序來驗(yàn)證方法的授權(quán)了。通過命令runclient.bat -client客戶端jar包文件名 -name 主類名來執(zhí)行客戶端程序。客戶端程序的容器將顯示一個(gè)對(duì)話框來提示用戶輸入用戶名和密碼(如圖15),填寫用戶名和密碼,按OK。
圖15
若用戶名或密碼與授權(quán)的方法不符,則會(huì)拋出沒有權(quán)限異常(如圖16)。
圖16
Countejb的部署描述文件ejb.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>count</display-name>
<enterprise-beans>
<session> // CountBean屬于session bean
<display-name>CountBean</display-name> //ejb組件的顯示名
<ejb-name>CountBean</ejb-name> //ejb組件名
<home>CountHome</home> //Home接口
<remote>Count</remote> //遠(yuǎn)程接口
<ejb-class>CountBean</ejb-class> //實(shí)現(xiàn)類
<session-type>Stateful</session-type> // CountBean屬于Stateful Bean
<transaction-type>Container</transaction-type> //CountBean事務(wù)類型為容器管理的
<security-identity> //安全標(biāo)識(shí)
<description></description>
<use-caller-identity></use-caller-identity> //CountBean使用調(diào)用者的身份標(biāo)識(shí)
</security-identity>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>admin</role-name> //定義安全角色admin
</security-role>
<method-permission> //將方法count和remove授權(quán)給安全角色admin
<role-name>admin</role-name>
<method> //方法定義
<ejb-name>CountBean</ejb-name>
<method-intf>Remote</method-intf>
<method-name>count</method-name>
<method-params />
</method>
<method>
<ejb-name>CountBean</ejb-name>
<method-intf>Home</method-intf>
<method-name>remove</method-name>
<method-params>
<method-param>java.lang.Object</method-param>
</method-params>
</method>
</method-permission>
<method-permission>
<unchecked /> //不檢查以下方法的授權(quán)
<method>
<ejb-name>CountBean</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getHandle</method-name>
<method-params />
</method>
.
.
.
.
<method>
<ejb-name>CountBean</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getEJBHome</method-name>
<method-params />
</method>
</method-permission>
<container-transaction> // CountBean的事務(wù)屬性
<method>
<ejb-name>CountBean</ejb-name>
<method-intf>Remote</method-intf>
<method-name>count</method-name>
<method-params />
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
|
可編程安全性和傳播調(diào)用者身份標(biāo)識(shí)的例子
此例程包括兩個(gè)部分,分別演示可編程安全性和調(diào)用者身份傳播。
可編程安全性例程
可編程安全性可應(yīng)用在web層和EJB層,分別是通過javax.servlet.http.HttpServletRequest接口的isUserInRole ()、getUserPrincipal ()方法和javax.ejb.EJBContext接口的isCallerInRole ()、getCallerPrincipal ()方法來實(shí)現(xiàn)的。 public boolean isUserInRole(java.lang.String role)方法此方法用來判斷調(diào)用者是否屬于某一特定的安全角色,如果屬于返回true,否則返回false。參數(shù)role指定某一安全角色。通過此方法開發(fā)者可以在程序代碼中加入自己的安全邏輯判斷,從而增強(qiáng)了J2EE在安全方面的靈活性。
public java.security.Principal getUserPrincipal()方法調(diào)用此方法可以得到一個(gè)java.security.Principal對(duì)象,此對(duì)象包含了調(diào)用者的用戶名,通過Principal.getName()方法可以得到用戶名。通過調(diào)用getUserPrincipal()方法開發(fā)者可以得到調(diào)用者的用戶名,然后對(duì)調(diào)用者的用戶名進(jìn)行特定的邏輯判斷。
public java.security.Principal getCallerPrincipal()方法 和public boolean isCallerInRole(java.lang.String roleName)方法的作用和方法同上。
下面我們通過例程來演示這些方法的用法
程序清單:
webtest.jsp
<%@page contentType="text/html"%>
<html>
<head><title>JSP Page</title></head>
<body>
<%-- <jsp:useBean id="beanInstanceName" scope="session" class="package.class" /> --%>
<%-- <jsp:getProperty name="beanInstanceName" property="propertyName" /> --%>
Hello!
the caller is <%=request.getUserPrincipal().getName()%><br/> <%--得到調(diào)用者的用戶名--%>
<% if (request.isUserInRole("admin")){%> <%--判斷調(diào)用者是否屬于"admin"安全角色--%>
the caller is admin Role;
<%} %>
</body>
</html>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
'-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
'http://java.sun.com/dtd/web-app_2_3.dtd'>
<web-app>
<display-name>WebApp</display-name>
<servlet>
<servlet-name>webtest</servlet-name>
<display-name>webtest</display-name>
<jsp-file>/webtest.jsp</jsp-file>
<security-role-ref>
<role-name>adminref</role-name>
<role-link>admin</role-link>
</security-role-ref>
<security-role-ref>
<role-name>guestref</role-name>
<role-link>guest</role-link>
</security-role-ref>
</servlet>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>webtest.jsp</welcome-file>
</welcome-file-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>WRCollection</web-resource-name>
<url-pattern>/webtest.jsp</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>guest</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name></realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>guest</role-name>
</security-role>
</web-app>
|
從web.xml文件的內(nèi)容可以看出,只有安全角色為"admin"和"guest"的用戶才有權(quán)對(duì)webtest.jsp文件進(jìn)行POST和GET操作。
運(yùn)行結(jié)果:
創(chuàng)建一個(gè)web應(yīng)用,將webtest.jsp作為一個(gè)web組件,并按照web.xml的內(nèi)容配置web應(yīng)用,在運(yùn)行環(huán)境中將用戶j2ee分配為admin安全角色,將用戶Tony分配為guest角色。發(fā)布web應(yīng)用到本地j2ee Server上。用ie訪問webtest.jsp 如圖17用用戶j2ee的身份進(jìn)行驗(yàn)證,用戶j2ee屬于admin安全角色。顯示結(jié)果見圖18
圖17
圖18
如果用用戶Tony進(jìn)行驗(yàn)證,結(jié)果見圖19
圖19
ejb中應(yīng)用可編程的安全性與在web中的方法相似,本文不再進(jìn)行介紹
傳播調(diào)用者身份標(biāo)識(shí)例程
本例程將演示調(diào)用者身份標(biāo)識(shí)如何在調(diào)用鏈中傳遞的,并且介紹如何應(yīng)用"Run As"來實(shí)現(xiàn)在調(diào)用鏈中更改調(diào)用者的身份。本例將用一個(gè)web組件(一個(gè)jsp文件)和兩個(gè)ejb組件來形成一個(gè)調(diào)用鏈。
程序清單:
webtest.jsp
<%@page contentType="text/html"%>
<%@page import="andy.*"%>
<%@page import="javax.naming.*"%>
<html>
<head><title>JSP Page</title></head>
<body>
<%-- <jsp:useBean id="beanInstanceName" scope="session" class="package.class" /> --%>
<%-- <jsp:getProperty name="beanInstanceName" property="propertyName" /> --%>
Hello!
the caller is <%=request.getUserPrincipal().getName()%> <br/>
<% if (request.isUserInRole("admin")){%>
the caller is admin Role;
<%} %>
<%
try {
Context ctx = new InitialContext();
andy.CountHome home =
(andy.CountHome)javax.rmi.PortableRemoteObject.narrow(
ctx.lookup("java:comp/env/CountHome"), andy.CountHome.class);
andy.Count count = home.create(1);
count.count();
}catch (Exception e)
{
e.printStackTrace();
}
%>
</body>
</html>
|
CountBean.java
package andy;
import javax.ejb.*;
import javax.naming.*;
public class CountBean implements SessionBean {
public int val;
private SessionContext EjbCxt = null;
public int count()
{
int temp = 0;
System.out.println("CountBean.count()");
//打印調(diào)用者名
System.out.println("the caller is "+EjbCxt.getCallerPrincipal().getName());
//判斷調(diào)用者的安全角色
if(EjbCxt.isCallerInRole("adminref")) // adminref為安全角色admin的引用名
{
System.out.println("the caller is admin Role");
}
if(EjbCxt.isCallerInRole("guestref")) // guestref為安全角色guest的引用名
{
System.out.println("the caller is guest Role");
}
if(EjbCxt.isCallerInRole("userref")) // userref為安全角色user的引用名
{
System.out.println("the caller is user Role");
}
//調(diào)用另一個(gè)ejb的遠(yuǎn)程方法
try {
Context ctx = new InitialContext();
CountHome1 home =
(CountHome1)javax.rmi.PortableRemoteObject.narrow(
ctx.lookup("java:comp/env/CountHome1"), CountHome1.class);
Count1 count = home.create(1);
temp = count.count();
}catch (Exception e)
{
e.printStackTrace();
}
return ++temp;
}
public void ejbCreate(int val) throws CreateException {
this.val = val;
}
public void ejbRemove() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void setSessionContext(SessionContext ctx) {
EjbCxt = ctx; //獲取EjbContext對(duì)象
}
}
CountBean1.java
package andy;
import javax.ejb.*;
public class CountBean1 implements SessionBean {
public int val;
private SessionContext EjbCxt = null;
public int count() {
System.out.println("CountBean1.count()");
System.out.println("the caller is "+EjbCxt.getCallerPrincipal().getName());
if(EjbCxt.isCallerInRole("adminref"))
{
System.out.println("the caller is admin Role");
}
if(EjbCxt.isCallerInRole("guestref"))
{
System.out.println("the caller is guest Role");
}
if(EjbCxt.isCallerInRole("userref"))
{
System.out.println("the caller is user Role");
}
return ++val;
}
public void ejbCreate(int val) throws CreateException {
this.val = val;
}
public void ejbRemove() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void setSessionContext(SessionContext ctx) {
EjbCxt = ctx;
}
}
|
以上的三個(gè)文件分別是一個(gè)web組件和兩個(gè)ejb組件的源代碼,這三個(gè)組件構(gòu)成了一個(gè)調(diào)用鏈.webtest.jsp中,首先通過HttpServletRequest..getUserPrincipal ()方法來得到調(diào)用webtest.jsp的用戶的Principal對(duì)象,在通過Principal.getName()方法得到調(diào)用者的用戶名. 然后通過HttpServletRequest..isUserInRole()方法來判斷調(diào)用這是否屬于特定的安全角色.CountBean是一個(gè)stateful SessoinBean,它擁有一個(gè)count()遠(yuǎn)程方法,在這個(gè)遠(yuǎn)程方法中寫了用于得到調(diào)用者用戶名和判斷調(diào)用這安全角色的代碼,還包括調(diào)用CountBean1對(duì)象的代碼,用于展示調(diào)用者身份標(biāo)識(shí)是如何在調(diào)用鏈中傳遞和調(diào)用者的安全角色如何被改變的.CountBean1也是一個(gè)stateful SessoinBean,它的代碼內(nèi)容與CountBean基本相同,只不過它不包含調(diào)用其他Bean的代碼.
現(xiàn)在我們應(yīng)該配置各組件的安全屬性了.我們?cè)诮M件webtest中創(chuàng)建安全角色admin,引用名為adminref,在組件CountBean和CountBean1中分別創(chuàng)建安全角色admin和user, 引用名分別為adminref和userref.將webtest組件配置為"HTTP Basic Authentication",給安全角色admin賦予訪問webtest.jsp的權(quán)限.把CountBean設(shè)置為Run As Specified Role,選擇user安全角色.在運(yùn)行環(huán)境中將用戶j2ee賦予admin安全角色和user安全角色.然后發(fā)布應(yīng)用到服務(wù)器.用用戶j2ee訪問webtest.jsp.
執(zhí)行結(jié)果:
客戶端見圖20
圖20
服務(wù)器端:
CountBean.count()
the caller is j2ee
the caller is admin Role
CountBean1.count()
the caller is j2ee
the caller is user Role
從運(yùn)行結(jié)果看,訪問webtest.jsp的用戶為j2ee,其安全角色為admin,訪問CountBean的用戶名為j2ee,安全角色為admin.可以看到用戶身份標(biāo)識(shí)從web容器傳遞到了ejb容器.再看組件CountBean1的輸出結(jié)果,由于CountBean被設(shè)置成了Run As Specified Role,因此在CountBean向下調(diào)用其他組件對(duì)象時(shí),用戶安全角色已經(jīng)被改為指定的安全角色,這里是user.所以我們會(huì)看到,調(diào)用組件CountBean1的用戶名為j2ee,安全角色為user.j2ee的這種安全特性滿足了同一用戶在不同應(yīng)用中具有不同安全角色的需求.開發(fā)人員也可以利用這種特性進(jìn)行靈活的安全邏輯判斷.
關(guān)于作者 王妍 某軟件公司系統(tǒng)架構(gòu)工程師、系統(tǒng)分析員,具有4年java開發(fā)工作的經(jīng)驗(yàn),對(duì)j2ee、ejb有較深的了解。 希望與廣大java愛好者進(jìn)行技術(shù)交流。Email: babaraNo_1@hotmail.com |