在
Jive
中除了前面介绍的有关设计模式实现组件外,还有其他有一定特点的组件功能,分析研究这些组件功能可以更加完整透彻地理解
Jive
论坛系统。
Jive
安全管理机制基本是由下列部分组成:
·
安全验证机制。主要是验证用户名和密码组合是否与数据库中注册时的数据一致,以确认该用户身份为注册用户。这是对所有的
JSP
访问都进行拦截访问。
·
访问权限控制(
ACL
)。对不同的数据不同用户拥有不同的访问权限,例如,一个帖子普通用户可以浏览,但是不能更该;但是管理员却可以编辑删除。这部分功能是通过代理模式实现,为每个关键数据都建立一个代理类用来实现访问权限检查,这在前面讨论过。
·
用户资料管理系统。主要是管理用户的资料数据,进行用户组和用户关系的建立等。
1
安全验证机制
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
这套安全验证机制对付小型系统的应用也是足够的。
2
用户资料管理
在
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
认为,如果给用户组赋予权限,那么用户组也接近角色的概念。