http://www-128.ibm.com/developerworks/cn/java/l-j2eesecurity2/index.html系統架構工程師、系統分析員
2002 年 9 月 02 日
在本系列文章的第一部分作者介紹了j2ee的安全概念、驗證模型和授權模型,這部分更偏重于理論的介紹。本文的第二部分作者將通過具體的例子向讀者展示如何在開發中應用j2ee提供的安全服務。本部分的重點在于應用與實踐。
注釋:本文的目的是介紹如何應用j2ee提供的安全服務,而并不針對特定的產品。因此作者選擇sun的 j2ee參考實現(j2sdkee)作為演示平臺。因為j2sdkee是完全遵照j2ee規范開發的,雖然它不像IBM WebSphere 、BEA WebLogic等j2ee產品那么產品化和商業化,但它絕對是學習j2ee的理想平臺。你可以通過 http://java.sun.com/j2ee/獲取sun 的j2ee參考實現的最新版本。本文選擇的是Sun的j2sdkee1.3.1。
本文將包括以下內容:
- 一個采用HTTP基本的驗證的例子
- 一個采用基于表單的驗證的例子
- 一個ejb方法授權的例子
- 一個可編程安全性和傳播調用者身份標識的例子
采用HTTP基本的驗證的例子
http基本驗證是Web客戶端驗證的一種,它和系統的授權機制一起控制受保護資源的訪問。
步驟:
1. 創建一個j2ee應用
在應用程序部署工具的File菜單選中New子菜單中的Application菜單項(見圖1)。會彈出新建應用程序對話框。填寫應用程序文件名和應用程序顯示名(見圖2)。
圖1
圖2
2. 創建一個web組件
在應用程序部署工具的File菜單選中New子菜單中的Web Compent菜單項,會彈出新建web組件向導對話框(見圖3)。選擇Create New WAR File in Application,在下拉框中選擇步驟1創建的應用test,在WAR Display Name框中填寫WebAppTest.點擊Content欄中的Eidt按鈕選擇此Web組件包含的文件。在這個例子中只有一個webtest.jsp文件。然后點擊Next,進入下一個對話框(見圖4)。由于我們的web組件是一個jsp文件,因此在組件類型中選擇JSP。然后一直按Next直到結束。此時我們已經創建了一個只包含一個jsp文件的web組件。接下來是配置安全屬性的步驟。
圖3
圖4
3. 配置安全屬性
3.1創建安全角色
在部署工具的左導航欄中點中步驟2創建的web組件WebAppTest,在右邊的屬性頁中選擇Roles屬性頁(見圖5)。點擊Add按鈕,在Name欄中填寫安全角色名user,Description欄填寫描述信息。安全角色代表具有相同安全權限用戶的集合。
圖5
3.2 配置安全策略
創建了安全角色后,應該對安全角色配置相應的安全策略。點擊Security屬性頁(見圖6)。
圖6
首先選擇你想用的驗證方式,從User Authentication Method下拉框中選擇Basic。這意味著你將通過基本的HTTP驗證方式驗證用戶。下面我們進行web資源的授權。點擊Security Constraint欄中的Add按鈕添加一條安全約束,約束名可以自定。接下來對創建好的約束添加Web資源。首先在Web Resource Collections中添加資源集合,然后選取資源集合包含的資源。此例中WRCollection資源集合中包含webtest.jsp文件,也可以包含各種屬于這個web組件的文件。接下來選擇哪些web操作要收到約束,j2sdkee1.3.1中只包含兩種操作(GET和POST),不同的產品支持的操作有所不同,在開發是應結合具體產品提供的操作來選取。現在應該指定安全角色了,點擊Authorized Roles欄中的Edit按鈕,會彈出安全角色列表對話框,從中選取已定義的安全角色。本例中選擇user。至此安全策略已經配置完畢,下面的步驟是將實際環境中的用戶和用戶組映射與安全角色進行映射。
4. 映射
在左導航欄中選中應用程序test在右邊選擇Security屬性頁(見圖7),在Role Name Reference欄中選中user,點擊正下方的Add按鈕,會彈出用戶和用戶組列表對話框,從中選擇要映射成安全角色user的用戶或組。此例中我們將用戶j2ee映射為安全角色user。這樣用戶J2ee將具有為安全角色user分配的訪問授權。
圖7
5. 部署應用
選中Web Context屬性頁,在Context Root文本框中填寫test,右鍵點擊左導航欄的應用test,在彈出菜單中選擇deploy完成應用程序的發布。至此我們完成了第一個例子的全部步驟。
部署描述文件
這個例子使用了說明性的安全服務,因此我們不需要編寫任何的安全相關的代碼,而是完全通過配置組件的部署描述文件來實現的。下面是這個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表達式
<http-method>GET</http-method> //受約束的資源操作方法
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint> //對安全角色授權
<role-name>user</role-name> //安全角色名
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config> //驗證方式設置
<auth-method>BASIC</auth-method> //使用基本的HTTP驗證方式
<realm-name></realm-name>
</login-config>
<security-role> //定義安全角色
<description>this is a user</description>
<role-name>user</role-name>
</security-role>
</web-app>
|
從部署描述文件可以知道這是一個名為WebAppTest的web組件,包含一個名為webtest.jsp的文件,只有被賦予user安全角色的用戶或用戶組才有權對webtest.jsp進行GET或POST操作。這里并沒有包含安全角色對實際用戶的映射,j2ee部署描述文件的DTD中并沒有定義用于安全角色和實際用戶的映射的元素,因為實際環境中有多種不同的用戶系統(如關系數據庫,系統文件形式和LDAP系統等)。因此安全角色和實際用戶的映射方式是由j2ee產品廠商制定的。
測試運行結果
打開ie,在導航欄輸入http://localhost:8000/test/webtest.jsp回車,會彈出驗證對話框,要求用戶提供用戶名和密碼(見圖8),輸入用戶名j2ee和密碼j2ee。通過用戶驗證后執行jsp文件,webtest.jsp打印出"hello!"(見圖9)。
圖8
圖9
注釋:在第一個例子中已經詳細的描述了各個步驟,在接下來的例子中會有一些與第一個例子相同的操作,因此對下面的例子只描述與第一個例子不同的步驟。
基于表單的驗證的例子
基于表單的驗證與基本的HTTP驗證的唯一區別是基本的HTTP驗證用瀏覽器提供的驗證信息對話框收集用戶驗證信息,而基于表單的驗證允許自定義登陸頁面來收集用戶驗證信息。本例子與第一個例子的步驟基本相同,不同的地方在于此例子要提供登陸頁面和出錯頁面。
登陸頁面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>
|
此文件有幾個地方值得注意:
- Action的值必須為"j_security_check"
- 獲取用戶名的域名必須是"j_username"
- 獲取用戶密碼的域必須是" j_password"
出錯頁面 error.html
<html>
用戶名或密碼不正確!
</html>
|
出錯頁面只是簡單的顯示出錯信息。
配置基于表單的驗證
首先將login.html和error.html加入到WebAppTest組件中。
然后見圖10選擇Security屬性頁,在User Authentication Method下拉框中選擇Form Based選項。點擊Settings…彈出用戶驗證設置對話框,在Login Page下拉框選login.html,在Error Page下拉框選error.html。
圖10
重新部署應用,再一次訪問http://localhost:8000/test/webtest.jsp 會出現login頁面(見圖11),如果用戶名或密碼錯誤,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> //使用基于表單的驗證方式
<realm-name>Default</realm-name> //使用缺省的安全域
<form-login-config>
<form-login-page>/login.html</form-login-page> //定義登陸頁面
<form-error-page>/error.html</form-error-page> //定義出錯頁面
</form-login-config>
</login-config>
<security-role>
<description>this is a user</description>
<role-name>user</role-name>
</security-role>
</web-app>
|
ejb方法授權的例子
從j2ee1.3開始便提供了對ejb的方法進行授權的安全服務,這種授權服務由ejb容器實現。當調用者調用ejb的方法時,ejb容器用調用者的身份來查找授予此調用者的訪問權限條目,如果調用者調用的方法屬于授權條目,那么ejb容器調用方法。否則,ejb容器拒絕調用此方法,并向調用者返回拒絕訪問異常。可以對遠程方法和home接口方法進行授權。本例中我們將對一個遠程方法和一個home接口方法進行授權。
首先創建一個session bean CountEjb
遠程接口 Count.java
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Count extends EJBObject {
/**
* 遠程方法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;
}
實現類 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;
//
// 遠程商業方法實現
public int count() {
System.out.println("count()");
return ++val;
}
//
// home接口Create方法的實現
//
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;
/*
創建并執行遠程方法
*/
System.out.println("Instantiating beans...");
count = home.create(countVal);
countVal = count.count();
System.out.println(countVal);
/*
remove Count對象
*/
count.remove();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
|
這個ejb包括一個遠程商業方法count(),我們將將此方法授權給某個安全角色。此外還將home接口的Create()方法授權給安全角色。
步驟1
編譯以上源程序并用j2sdkee1.3.1的組裝發布工具(deploytool.bat)進行組裝(如圖12)。
圖12
步驟2:配置安全角色
在對方法進行授權前,必須創建將被授權的安全角色。創建安全角色的步驟前邊已經介紹過了,此處不再重復。本例中我們創建名為admin的安全角色。
步驟3:方法授權
方法授權的過程是確定那些安全角色可以訪問特定方法的過程。方法授權一般是應用程序組裝或應用程序部署者的責任。他們根據企業特定的需求創建不同的安全角色,并授予這些安全角色特定的訪問權限。
用鼠標選中CountBean,在右端的窗口選擇Security屬性頁(如圖13),在Security Identity選項中選擇Use Caller ID,這意味著ejb容器將用方法調用者的身份來驗證方法調用權限。 Run As Specified Role選項將在"傳播調用者身份標識的例子"進行介紹。由于在前面創建了admin安全角色,因此你可以看到Method Permissions欄中出現admin列。首先對遠程方法count()進行授權。選擇Remote選項,并在count()方法的Availability列中選擇Sel Roles,然后選中count()方法的admin列。到此為止我們已對遠程方法count()進行了授權。接下來對home接口的create()方法進行授權。在Show欄中選擇Remote Home,剩下的步驟與count()方法授權相同。我們已經將count()方法和create()方法授權給了admin安全角色。但安全角色這是一個邏輯的集合,并不代表具體的用戶或用戶組,因此結下來我們要做的就是將安全角色與實際的用戶映射起來。
步驟4:角色映射
首先我們需要在我們的j2ee環境中創建一個用戶,用戶起名為Tony,密碼為1。
圖13
這里我們使用用戶名和密碼的方式進行身份驗證。我們在default Realm中創建此用戶。可以使用命令行方式:"realmtool -add Tony 1 eng"。詳細的使用方法參見j2sdk1.3.1文檔的工具部分。接下來映射安全角色到用戶。選中ejb應用CountEjb,在右邊窗口中選擇Security屬性頁(如圖14),點擊Edit Roles按鈕,選擇安全角色admin。再點擊Add按鈕選擇Tony用戶。這樣已經將安全角色admin和用戶Tony映射起來了。
步驟5:部署應用
部署應用到本地機,右鍵點擊ejb應用CountEjb,選擇彈出菜單的deploy項,按要求配置各項。
步驟6:創建客戶端
創建客戶端將客戶端程序的主類和其他輔助類打包。創建端將客的過程比較簡單,這里就不作描述了。
步驟7:運行程序
現在可以運行客戶端程序來驗證方法的授權了。通過命令runclient.bat -client客戶端jar包文件名 -name 主類名來執行客戶端程序。客戶端程序的容器將顯示一個對話框來提示用戶輸入用戶名和密碼(如圖15),填寫用戶名和密碼,按OK。
圖15
若用戶名或密碼與授權的方法不符,則會拋出沒有權限異常(如圖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> //遠程接口
<ejb-class>CountBean</ejb-class> //實現類
<session-type>Stateful</session-type> // CountBean屬于Stateful Bean
<transaction-type>Container</transaction-type> //CountBean事務類型為容器管理的
<security-identity> //安全標識
<description></description>
<use-caller-identity></use-caller-identity> //CountBean使用調用者的身份標識
</security-identity>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>admin</role-name> //定義安全角色admin
</security-role>
<method-permission> //將方法count和remove授權給安全角色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 /> //不檢查以下方法的授權
<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的事務屬性
<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>
|
可編程安全性和傳播調用者身份標識的例子
此例程包括兩個部分,分別演示可編程安全性和調用者身份傳播。
可編程安全性例程
可編程安全性可應用在web層和EJB層,分別是通過javax.servlet.http.HttpServletRequest接口的isUserInRole ()、getUserPrincipal ()方法和javax.ejb.EJBContext接口的isCallerInRole ()、getCallerPrincipal ()方法來實現的。 public boolean isUserInRole(java.lang.String role)方法此方法用來判斷調用者是否屬于某一特定的安全角色,如果屬于返回true,否則返回false。參數role指定某一安全角色。通過此方法開發者可以在程序代碼中加入自己的安全邏輯判斷,從而增強了J2EE在安全方面的靈活性。
public java.security.Principal getUserPrincipal()方法調用此方法可以得到一個java.security.Principal對象,此對象包含了調用者的用戶名,通過Principal.getName()方法可以得到用戶名。通過調用getUserPrincipal()方法開發者可以得到調用者的用戶名,然后對調用者的用戶名進行特定的邏輯判斷。
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/> <%--得到調用者的用戶名--%>
<% if (request.isUserInRole("admin")){%> <%--判斷調用者是否屬于"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文件的內容可以看出,只有安全角色為"admin"和"guest"的用戶才有權對webtest.jsp文件進行POST和GET操作。
運行結果:
創建一個web應用,將webtest.jsp作為一個web組件,并按照web.xml的內容配置web應用,在運行環境中將用戶j2ee分配為admin安全角色,將用戶Tony分配為guest角色。發布web應用到本地j2ee Server上。用ie訪問webtest.jsp 如圖17用用戶j2ee的身份進行驗證,用戶j2ee屬于admin安全角色。顯示結果見圖18
圖17
圖18
如果用用戶Tony進行驗證,結果見圖19
圖19
ejb中應用可編程的安全性與在web中的方法相似,本文不再進行介紹
傳播調用者身份標識例程
本例程將演示調用者身份標識如何在調用鏈中傳遞的,并且介紹如何應用"Run As"來實現在調用鏈中更改調用者的身份。本例將用一個web組件(一個jsp文件)和兩個ejb組件來形成一個調用鏈。
程序清單:
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()");
//打印調用者名
System.out.println("the caller is "+EjbCxt.getCallerPrincipal().getName());
//判斷調用者的安全角色
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");
}
//調用另一個ejb的遠程方法
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對象
}
}
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;
}
}
|
以上的三個文件分別是一個web組件和兩個ejb組件的源代碼,這三個組件構成了一個調用鏈.webtest.jsp中,首先通過HttpServletRequest..getUserPrincipal ()方法來得到調用webtest.jsp的用戶的Principal對象,在通過Principal.getName()方法得到調用者的用戶名. 然后通過HttpServletRequest..isUserInRole()方法來判斷調用這是否屬于特定的安全角色.CountBean是一個stateful SessoinBean,它擁有一個count()遠程方法,在這個遠程方法中寫了用于得到調用者用戶名和判斷調用這安全角色的代碼,還包括調用CountBean1對象的代碼,用于展示調用者身份標識是如何在調用鏈中傳遞和調用者的安全角色如何被改變的.CountBean1也是一個stateful SessoinBean,它的代碼內容與CountBean基本相同,只不過它不包含調用其他Bean的代碼.
現在我們應該配置各組件的安全屬性了.我們在組件webtest中創建安全角色admin,引用名為adminref,在組件CountBean和CountBean1中分別創建安全角色admin和user, 引用名分別為adminref和userref.將webtest組件配置為"HTTP Basic Authentication",給安全角色admin賦予訪問webtest.jsp的權限.把CountBean設置為Run As Specified Role,選擇user安全角色.在運行環境中將用戶j2ee賦予admin安全角色和user安全角色.然后發布應用到服務器.用用戶j2ee訪問webtest.jsp.
執行結果:
客戶端見圖20
圖20
服務器端:
CountBean.count()
the caller is j2ee
the caller is admin Role
CountBean1.count()
the caller is j2ee
the caller is user Role
從運行結果看,訪問webtest.jsp的用戶為j2ee,其安全角色為admin,訪問CountBean的用戶名為j2ee,安全角色為admin.可以看到用戶身份標識從web容器傳遞到了ejb容器.再看組件CountBean1的輸出結果,由于CountBean被設置成了Run As Specified Role,因此在CountBean向下調用其他組件對象時,用戶安全角色已經被改為指定的安全角色,這里是user.所以我們會看到,調用組件CountBean1的用戶名為j2ee,安全角色為user.j2ee的這種安全特性滿足了同一用戶在不同應用中具有不同安全角色的需求.開發人員也可以利用這種特性進行靈活的安全邏輯判斷.