Jive安全管理機制
在Jive中除了前面介紹的有關設計模式實現組件外,還有其他有一定特點的組件功能,分析研究這些組件功能可以更加完整透徹地理解Jive論壇系統。
Jive安全管理機制基本是由下列部分組成:
· 安全驗證機制。主要是驗證用戶名和密碼組合是否與數據庫中注冊時的數據一致,以確認該用戶身份為注冊用戶。
這是對所有的JSP訪問都進行攔截訪問。
· 訪問權限控制(ACL)。對不同的數據不同用戶擁有不同的訪問權限,例如,一個帖子普通用戶可以瀏覽,但是不能
更該;但是管理員卻可以編輯刪除。這部分功能是通過代理模式實現,為每個關鍵數據都建立一個代理類用來實現訪問
權限檢查,這在前面討論過。
· 用戶資料管理系統。主要是管理用戶的資料數據,進行用戶組和用戶關系的建立等。
安全驗證機制
Jive的安全驗證機制是按照比較通用的思路設計的。類似前面“簡單的用戶注冊管理系統”中的介紹,Jive也是在所有
的JSP頁面中include一個安全檢驗功能的global.jsp。由于global.jsp是在每個JSP一開始必須執行的功能,因此通過
攔截global.jsp攔截發往各個JSP頁面的請求(request)。如果這個請求是合法的,將被允許通過;如果不是,
將注明請求者身份是Anonymous(匿名者)。
global.jsp代碼如下:
boolean isGuest = false;
Authorization authToken = SkinUtils.getUserAuthorization(request, response);

if (authToken == null)
{//未被驗證通過
authToken = AuthorizationFactory.getAnonymousAuthorization();
isGuest=true;
}
在Jive中,以Authorization對象作為驗證通過的標志,它的接口代碼如下:


public interface Authorization
{
public long getUserID();
public boolean isAnonymous();
}

具體實現是DbAuthorization,代碼如下:

public final class DbAuthorization implements Authorization, Serializable
{
private long userID;

protected DbAuthorization(long userID)
{
this.userID = userID;
}

public long getUserID()
{
return userID;
}

public boolean isAnonymous()
{
return userID == -1;
}
}
此類只是一個userID,因此只是一個象征性的標志。
SkinUtils是一個為JSP服務的類,它的getUserAuthorization代碼如下:
public static Authorization getUserAuthorization
(HttpServletRequest request, HttpServletResponse response)

{
HttpSession session = request.getSession();
// 從HttpSession中獲取Authorization實例
Authorization authToken =
(Authorization)session.getAttribute(JIVE_AUTH_TOKEN);

if (authToken != null)
{ return authToken; }
// 如果HttpSession中沒有,檢查用戶瀏覽器cookie
Cookie cookie = getCookie(request, JIVE_AUTOLOGIN_COOKIE);

if (cookie != null)
{

try
{
String[] values = decodePasswordCookie(cookie.getValue());
String username = values[0];
String password = values[1];
//從cookie中獲得用戶名和密碼后,進行安全驗證
authToken = AuthorizationFactory.getAuthorization(username,password);

}catch (Exception e)
{}
// put that token in the user's session:

if (authToken != null)
{//如果通過驗證,保存authToken在http Session
session.setAttribute(JIVE_AUTH_TOKEN, authToken);
}
// return the authorization token
return authToken;
}
return null;
}
用戶驗證預先通過兩個步驟。首先檢查HttpSession中是否保存了該用戶的驗證信息,如果用戶第一次驗證通過,反復
訪問,這道關口檢查就可以通過。
如果HttpSession中沒有驗證信息,那么從該用戶的瀏覽器cookie中尋找用戶名和密碼。如果該用戶激活了cookie保存
這些登錄信息,那么應該可以找到用戶名和密碼,這樣就省卻了用戶再次從鍵盤輸入用戶名和密碼,將用戶名和密碼
通過下列語句進行數據庫驗證:
authToken = AuthorizationFactory.getAuthorization(username,password);
這一舉是驗證關鍵。AuthorizationFactory是一個抽象類,定義了Jive安全驗證機制所需的所有方法,AuthorizationFactory的實現類似前面討論
的ForumFactory實現,是使用工廠模式加動態類反射機制完成的,代碼如下:


public abstract class AuthorizationFactory
{
//定義一個數據庫具體實現
private static String className =
" com.Yasna.forum.database.DbAuthorizationFactory";
private static AuthorizationFactory factory = null;
//驗證方法 如果沒有UnauthorizedException拋出,表示驗證通過
public static Authorization getAuthorization(String username,
String password) throws UnauthorizedException

{
loadAuthorizationFactory();
return factory.createAuthorization(username, password);
}

//匿名者處理方法

public static Authorization getAnonymousAuthorization()
{
loadAuthorizationFactory();
return factory.createAnonymousAuthorization();
}
//需要具體實現的抽象方法
protected abstract Authorization createAuthorization(String username,
String password) throws UnauthorizedException;
protected abstract Authorization createAnonymousAuthorization();

//動態配置AuthorizationFactory的具體實現,可以在配置文件中定義一個
//基于LDAP的實現。類似ForumFactory的getInstance方法

private static void loadAuthorizationFactory()
{
…
}
}
AuthorizationFactory看上去很復雜,實際只有一個核心方法getAuthorization。實現用戶名和密碼的驗證。如果無法通
過驗證,有兩個信息實現顯示:一個是拋出UnauthorizedException,另外一個是返回空的Authorization對象。
那么,子類DbAuthorizationFactory毫無疑問就是查詢數據庫,將輸入的用戶名和密碼與數據庫保存的用戶名和密碼進行校驗。
Jive的安全驗證機制比較簡單易懂,值得在實踐中學習借鑒。但是注意到這套安全驗證機制只是Web層的“手工”驗證,資源
訪問權限(ACL)也是自己“手工”來實現的。如果使用EJB技術,因為EJB容器本身有一定的資源訪問控制體系,因此在Web
層驗證通過后,需要將這些登錄信息傳遞到EJB層。當然如果直接使用Web容器的安全驗證機制,那么Web層與EJB層之間的
登錄信息傳遞將由容器實現,這樣就更加簡單方便。
Jive這種的安全驗證并不是使用Web容器的安全驗證機制,如何使用Web容器的安全驗證機制將在以后章節介紹。盡管如此
,Jive這套安全驗證機制對付小型系統的應用也是足夠的。
用戶資料管理
在Jive中,用戶User對象的操作訪問類似于論壇Forum對象的訪問,與User對象有關的操作都封裝在一個類中操作,這是
外觀(Facade)模式的應用。
在Jive中,用戶資料管理屬于大系統中的一個子系統,在這個子系統中,用戶子系統和其他系統又有一定的關系,涉及
的類不少,通過建立一個UserManager類來統一對外接口,使得整個子系統條目結構清晰。
UserManager中無外乎用戶數據的管理,如用戶的創建、修改、查詢和刪除。DbUserManager是UserManager的一個數據庫
實現,可是看看DbUserManager中除了刪除功能是直接通過SQL語句進行數據庫刪除操作外,其他都委托給User的具體實
現DbUser實現的。這種實現非常類似于EJB中Session Bean和實體Bean之間的關系。以創建用戶資料為例,代碼如下:
public User createUser(String username, String password, String email)
throws UserAlreadyExistsException

{
User newUser = null;

try
{
//以username查詢改用戶是否存在
User existingUser = getUser(username);
//如果沒有拋出UserNotFoundException異常,表示該用戶存在
//The user already exists since now exception, so:
throw new UserAlreadyExistsException();

} catch (UserNotFoundException unfe)
{
//該用戶不存在,創建一個新用戶
newUser = new DbUser(username, password, email, factory);
}
return newUser;
}

DbUser的構造方法實際是用戶資料的新增創建:

protected DbUser(String username, String password, String email,
DbForumFactory factory)


{
this.id = SequenceManager.nextID(JiveGlobals.USER); //獲得自增ID
this.username = username;
// Compute hash of password.
this.passwordHash = StringUtils.hash(password); //獲得加密的密碼
this.email = email;
this.factory = factory;
long now = System.currentTimeMillis();
creationDate = new java.util.Date(now);
modifiedDate = new java.util.Date(now);
properties = new Hashtable();
insertIntoDb(); //數據庫插入數據
}

在Jive中,數據修改的保存是由DbUser的saveToDb方法實現的,而saveToDb方法調用是在每個setXXXX方法中。即每當外界
調用DbUser的setXXXX,則表示需要改變某些字段屬性值,在這個方法中直接進行數據庫存儲,這也類似EJB中
CMP實體Bean的數據字段修改保存。
Jive中組Group與用戶User處理幾乎差不多,只是在Group中整合了權限方面的信息,這種做法是有一定的局限性,不是很
值得借鑒,要想設計一個動態擴展靈活的權限系統,必須在用戶或組與權限之間引入角色概念,也就是比較先進的基于
角色的權限系統(RBAC Roled-Based Access Control,相關網址:http://csrc.nist.gov/rbac/)。
在RBAC中,用戶組只是用戶的一個集合,應該是通過角色和權限發生聯系。所以RBAC認為,如果給用戶組賦予權限,
那么用戶組也接近角色的概念。