基于容器的用户安全管理系统和JMS(3)

<script type="text/javascript"><!-- google_ad_client = "pub-2947489232296736"; /* 728x15, 创建于 08-4-23MSDN */ google_ad_slot = "3624277373"; google_ad_width = 728; google_ad_height = 15; //--> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"><!-- google_ad_client = "pub-2947489232296736"; /* 160x600, 创建于 08-4-23MSDN */ google_ad_slot = "4367022601"; google_ad_width = 160; google_ad_height = 600; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

  3 详细设计和实现

 

 

 

  图6-5 业务模型图

 

 

 

  本项目功能需求中用户资料管理功能将需要详细的类设计和实现,而权限的验证和授权以及可控制的资源限制访问两个功能是由J2EE容器实现的,需要进行详细的配置和设置。下面就分这两个方向将本项目逐步具体实现。

  3.1 业务对象建模

  首先确定本项目的Domain Model,或者可以称为基本业务对象,如图6-5所示。

  Role代表角色,User代表用户,group代表用户组,用户组是用户的集合,多个用户可以对应同一个角色。角色定义需要是灵活的,可以增减修改的。角色Role接口如下:

  public interface Role extends java.io.Serializable{

  public String getRoleId();

  public void setRoleId(String roleId);

  //获得角色名称

  public String getName();

  public void setName(String name);

  }

  用户User的接口定义:

  public interface User extends java.io.Serializable{

  //用户的名称

  public String getName();

  public void setName(String name) ;

  //用户Id

  public String getUserId() ;

  public void setUserId(String userId) ;

  //用户密码

  public String getPassword();

  public void setPassword(String password);

  //用户E-mail

  public String getEmail();

  public void setEmail(String email);

  }

  在用户接口定义中,只定义关键常用字段的操作行为,有关用户的地址、电话等其他信息使用其他对象表示,可以根据具体应用系统不同的要求再设计。

  用户组group的接口定义如下:

  public interface Group extends java.io.Serializable{

  public String getGroupId();

  public void setGroupId(String groupId);

  //用户组名称

  public String getName();

  public void setName(String name);

  }

  用户组是用来代表一组用户,可以指定一个用户组为一个特定角色,那么该用户组下的所有用户也将拥有该角色的访问权限能力。

  用户和角色的直接关系设定为多对一,每个用户只能为一个角色,这样可以使问题变得简单些。

  3.2 数据库设计

  依据业务对象模型建立相应的数据模型,同时使用专门关联表来实现业务对象之间的关系。在EJB 2.0以上容器中,可以使用实体Bean的CMR来表示一对多、多对一或多对多的关系。这样就无需写很多代码,但是使用数据表来表示关系更容易把握,更方便理解和使用。

  以MySQL数据库为例,下面是用户User的数据表结构:

  CREATE TABLE user (

  userId varchar(50) binary NOT NULL default ', #用户Id

  password varchar(50) binary default NULL, #密码

  name varchar(50) default NULL, #用户名

  email varchar(50) default NULL, #E-mail邮件地址

  PRIMARY KEY (userId),

  UNIQUE KEY email (email),

  UNIQUE KEY name (name)

  ) TYPE=InnoDB; #使用MySQL的InnoDB

  以下是角色Role的数据表结构:

  CREATE TABLE role (

  roleId varchar(50) binary NOT NULL default ', #角色Id

  name varchar(100) default NULL, #角色名称

  PRIMARY KEY (roleId)

  ) TYPE=InnoDB;

  使用下表保存用户和角色之间关系:

  CREATE TABLE users_roles (

  userId varchar(50) NOT NULL default ', #用户Id

  roleId varchar(50) NOT NULL default ' #角色Id

  ) TYPE=InnoDB;

  用户组group、用户组与用户关系以及用户组与角色关系与此类似。

  3.3 实体bean实现

  在EJB层将实现用户资料管理的主要功能,可以使用EJB CMP实现各个数据模型,其中User的实体bean如下。

  Bean实现:

  import javax.ejb.*;

  abstract public class UserBean implements EntityBean {

  EntityContext entityContext;

  public java.lang.String ejbCreate(java.lang.String userId) throws CreateException {

  setUserId(userId);

  return null;

  }

  public void ejbPostCreate(java.lang.String userId) throws CreateException {

  }

  public void ejbRemove() throws RemoveException {

  }

  public abstract void setUserId(java.lang.String userId);

  public abstract void setPassword(java.lang.String password);

  public abstract void setName(java.lang.String name);

  public abstract void setEmail(java.lang.String email);

  public abstract java.lang.String getUserId();

  public abstract java.lang.String getPassword();

  public abstract java.lang.String getName();

  public abstract java.lang.String getEmail();

  public void ejbLoad() {

  }

  public void ejbStore() {

  }

  public void ejbActivate() {

  }

  public void ejbPassivate() {

  }

  public void unsetEntityContext() {

  this.entityContext = null;

  }

  public void setEntityContext(EntityContext entityContext) {

  this.entityContext = entityContext;

  }

  }

  Local Home接口为:

  import javax.ejb.*;

  import java.util.*;

  public interface UserHome extends javax.ejb.EJBLocalHome {

  public UserLocal create(String userId) throws CreateException;

  public UserLocal findByEmail(String email) throws FinderException;

  public UserLocal findByName(String name) throws FinderException;

  public UserLocal findByPrimaryKey(String userId) throws FinderException;

  }

  Local接口为:

  import javax.ejb.*;

  import java.util.*;

  public interface UserLocal extends javax.ejb.EJBLocalObject {

  public String getUserId();

  public void setPassword(String password);

  public String getPassword();

  public void setName(String name);

  public String getName();

  public void setEmail(String email);

  public String getEmail();

  }

  相应的ejb-jar.xml为:

 

  User

  User

  com.jdon.security.auth.ejb.UserHome

  com.jdon.security.auth.ejb.UserLocal

  com.jdon.security.auth.ejb.UserBean

  Container

  java.lang.String

  False

  2.x

  User

 

  userId

 

 

  password

 

 

  name

 

 

  email

 

  userId

 

 

  findByEmail

 

  java.lang.String

 

 

  SELECT OBJECT(s) FROM User AS s WHERE s.email=?1

 

 

 

  findByName

 

  java.lang.String

 

 

  SELECT OBJECT(s) FROM User AS s WHERE s.name=?1

 

 

  在该实体bean中,使用EJB-QL实现了以E-mail或用户名为关键字的查询语句。

  其他数据表都可以采取类似上述方法建立,当然使用Jbuilder专门的EJB可视化开发工具,可以直接从数据库中将这些表自动导入成相应的实体bean,降低开发量。

  3.4 Session Bean实现

  本项目需要一个Facade类统一实现用户资料的操作,建立无状态Session Bean 为UserManager的EJB。在这个类中,主要实现有关用户的新增、删除或修改。代码如下:

  public class UserManagerBean implements SessionBean {

  private final static Logger logger = Logger.getLogger(UserManagerBean.class);

  SessionContext sessionContext;

  UserHome userHome;

  UsersRolesHome usersRolesHome;

  UsersGroupsHome usersGroupsHome;

  RoleManagerLocalHome roleManagerLocalHome;

  SequenceGeneratorLocalHome sequenceHome;

  // ejbCreate()一般只执行一次,以后再调用时不再执行,通过ejbCreate()可以

  //实现一些类属性的缓冲

  public void ejbCreate() throws CreateException {

  try {

  ServiceLocator serviceLocator = new ServiceLocator();

  userHome = (UserHome) serviceLocator.getLocalHome(

  JNDINames.USER_HOME);

  sequenceHome = (SequenceGeneratorLocalHome)

  serviceLocator.getLocalHome(JNDINames.SEQUENCEGENERATOR_HOME);

  usersRolesHome = (UsersRolesHome) serviceLocator.getLocalHome(

  JNDINames.USERSROLES_HOME);

  usersGroupsHome = (UsersGroupsHome) serviceLocator.getLocalHome(

  JNDINames.USERSGROUPS_HOME);

  roleManagerLocalHome = (RoleManagerLocalHome)

  serviceLocator.getLocalHome( JNDINames.ROLEMANAGER_HOME);

  } catch (Exception ex) {

  logger.error("Service Locate error:" + ex);

  throw new CreateException();

  }

  }

  //从Sequence EJB组件获得自增的序列号

  public int getNewId(String name) {

  try {

  SequenceGeneratorLocal seq = sequenceHome.create();

  return seq.nextSequenceNumber(name);

  } catch (Exception ex) {

  throw new EJBException("Error generating id for : " + name + ex);

  }

  }

  //创建一个用户

  public void createUser(UserEvent userEvent) {

  User userDTO = userEvent.getUser();

  if (nameIsExisted(userDTO)){

  logger.debug("name :" + userDTO.getName() + " has been exsited");

  userEvent.setErrors(Constants.NAME_EXISTED);

  return;

  }

  if (emailIsExisted(userDTO)){

  logger.debug("eamil :" + userDTO.getEmail() + " has been exsited");

  userEvent.setErrors(Constants.EMAIL_EXISTED);

  return;

  }

  UserLocal userLocal = null;

  try {

  String id = Integer.toString(getNewId(Constants.SEQUENCE_USER_NAME));

  userLocal = userHome.create(id);

  userDTO.setUserId(id);

  updateUser(userEvent);

  //创建该用户的默认角色

  createUsersRoles(userDTO);

  } catch (Exception ex) {

  logger.error(ex);

  throw new EJBException("create user error : " + ex);

  }

  }

  //检查是否有重复用户名

  private boolean nameIsExisted(User userDTO) {

  boolean found = false;

  User user = getUserByName(userDTO.getName());

  if (user != null)

  found = true;

  return found;

  }

  //检查是否有重复E-mail

  private boolean emailIsExisted(User userDTO) {

  boolean found = false;

  User user = getUserByEmail(userDTO.getEmail());

  if (user != null)

  found = true;

  return found;

  }

  //创建用户默认角色,也就是建立用户和角色的对应关系

  public void createUsersRoles(User userDTO) throws Exception {

  UsersRoles usersRoles = null;

  try {

  RoleManagerLocal roleManagerLocal = roleManagerLocalHome.create();

  String roleId = roleManagerLocal.getRoleId(Role.USER);

  usersRoles = usersRolesHome.create(userDTO.getUserId(), roleId);

  } catch (Exception ex) {

  logger.error(ex);

  throw new EJBException("createUsersRoles error : " + ex);

  }

  }

  //修改用户资料

  public void updateUser(UserEvent userEvent) {

  User userDTO = userEvent.getUser();

  UserLocal userLocal = null;

  try {

  userLocal = userHome.findByPrimaryKey(userDTO.getUserId());

  userLocal.setName(userDTO.getName());

  userLocal.setEmail(userDTO.getEmail());

  userLocal.setPassword(userDTO.getPassword());

  } catch (Exception ex) {

  logger.error(ex);

  }

  }

  //以E-mail获得用户

  public User getUserByEmail(String emailAddress) {

  emailAddress = emailAddress.trim().toLowerCase();

  UserLocal userLocal = null;

  try {

  userLocal = userHome.findByEmail(emailAddress);

  } catch (Exception ex) {

  logger.warn(ex);

  }

  return getUser(userLocal);

  }

  //以Id查询用户

  public User getUserById(String userId) {

  userId = userId.trim().toLowerCase();

  logger.debug(" userId =" + userId);

  UserLocal userLocal = null;

  try {

  userLocal = userHome.findByPrimaryKey(userId);

  } catch (Exception ex) {

  logger.warn(ex);

  }

  return getUser(userLocal);

  }

  //获得用户User实例 DTO模式

  private User getUser(UserLocal userLocal) {

  if (userLocal == null)

  return null;

  logger.debug(" userId =" + userLocal.getUserId());

  User user = new UserModel(userLocal.getUserId());

  user.setEmail(userLocal.getEmail());

  user.setName(userLocal.getName());

  user.setPassword(userLocal.getPassword());

  user.setUserId(userLocal.getUserId());

  return user;

  }

  //获得用户的Principal

  public User getUserByPrincipal() throws Exception, PrincipalException {

  Principal principal = null;

  try {

  principal = sessionContext.getCallerPrincipal();

  } catch (Exception e) {

  logger.error(e);

  throw new PrincipalException();

  }

  if (principal == null) {

  throw new PrincipalException();

  }

  String name = principal.getName();

  return getUserByName(name);

  }

  …

  }

  在UserManager中基本实现了用户资料的相关操作。在本项目中,还有用户组以及角色的相关操作。为避免UserManager中包含过多逻辑,需要再建立一个无状态Session Bean,如RoleManager。

  用户注册登录后,其Session生存周期的活动将一直维持到其离开系统。因此可以建立一个有状态Session Bean保存用户的资料如User实例,这样不必经常到数据库读取。有状态Session Bean还可以作为一个总的Facade类,分别包含其他Facade群,这样,系统会显得条理分明。创建有状态Session Bean代码如下:

  public class SecurityFacadeBean extends EJBControllerBean {

  private final static Logger logger = Logger.getLogger(SecurityFacadeBean.class);

  SessionContext sessionContext;

  RoleManagerLocalHome roleManagerLocalHome;

  UserManagerLocalHome userManagerLocalHome;

  AsyncSenderLocalHome asyncSenderLocalHome;

  private User user = null;

  …

  //获得Facade类 UsermanagerLocal

  public UserManagerLocal getUserManager() {

  UserManagerLocal userManagerLocal = null;

  try {

  userManagerLocal = userManagerLocalHome.create();

  } catch (Exception ex) {

  logger.error("getUserManager() error:" + ex);

  }

  return userManagerLocal;

  }

  //获得Facade类RoleManagerLocal

  public RoleManagerLocal getRoleManager() {

  RoleManagerLocal roleManagerLocal = null;

  try {

  roleManagerLocal = roleManagerLocalHome.create();

  } catch (Exception ex) {

  logger.error("getRoleManager() error:" + ex);

  }

  return roleManagerLocal;

  }

  //获得当前session的User实例

  public User getUser() {

  if (this.user != null)

  return this.user;

  logger.debug("user is null, get it from principal ...");

  setUser();

  return this.user;

  }

  //密码丢失查询

  public boolean getPassword(String email) {

  logger.debug("--> enter getpassword");

  boolean success = false;

  try {

  User user = getUserManager().getUserByEmail(email);

  if (user == null)

  return success;

  String subject = " 用户名和密码";

  StringBuffer buffer = new StringBuffer();

  buffer.append(" 用户:").append(user.getName());

  buffer.append(" 密码:").append(user.getPassword());

  if (sendMail(user.getEmail(), subject, buffer.toString()))

  success = true;

  } catch (Exception ex) {

  logger.error(" getPassword: " + ex);

  }

  return success;

  }

  //调用E-mail发送组件,通过JMS发出E-mail

  public boolean sendMail(String toAddress, String subject, String content) {

  try {

  logger.debug(" -->enter send mail");

  Mail mail = new Mail();

  mail.setToAddress(toAddress);

  mail.setFromAddress("banq@jdon.com");

  mail.setSubject(subject);

  mail.setContent(content);

  String msg = MailUtil.getMsgFromMail(mail);

  AsyncSenderLocal asyncSenderLocal = asyncSenderLocalHome.create();

  asyncSenderLocal.sendAMessage(msg);

  logger.debug(" -->send mail to: " + toAddress + " successfully!");

  return true;

  } catch (Exception ex) {

  logger.error(" sendMail() error : " + ex);

  return false;

  }

  }

  //判断当前用户是否是管理员

  public boolean isAdministrator() {

  return sessionContext.isCallerInRole(Role.ADMINISTRATOR);

  }

  //判断当前用户是否是普通用户

  public boolean isUser() {

  return sessionContext.isCallerInRole(Role.USER);

  }

  …

  }

  可以看到,SecurityFacadeBean是继承接口框架系统中的EJBController,SecurityFacadeBean作为一个总的Facade类,通过接口框架负责和Web实现联系,如图6-6所示。

 

  图6-6 EJB Facade群

  SecurityFacadeBean中isCallerInRole是用来判断当前用户是否属于可以访问该资源的角色,访问该资源角色的权限在ejb-jar.xml中定义。

  3.5 EJB容器安全配置

  J2EE容器的安全管理框架以角色为联系纽带,分两个方向。一个是用户资料系统;另外一个是访问权限系统。后者是通过web.xml或ejb-jar.xml配置实现的。

  在本项目中,为了限制角色对某些类或方法的访问权限,可以在ejb-jar.xml中设置。

 

 

  the role is super user

  Admin

 

 

  register user

  User

 

 

  User

 

  RoleManager

  *

 

 

  …

 

  在本EJB中定义了两个角色:Admin和User(当然可以根据实际情况定义角色)。具体的权限是在method-permission中设置,可以指定某个类的具体方法,“*”表示所有的方法。

  既然定义了角色,那么在具体类的配置中也需要声明具体角色的指向,如SecurityFacadeBean的ejb-jar配置如下:

 

  SecurityFacade

  SecurityFacade

 

  com.jdon.security.auth.ejb.SecurityFacadeLocalHome

 

  com.jdon.security.auth.ejb.SecurityFacadeLocal

  com.jdon.security.auth.ejb.SecurityFacadeBean

  Stateful

  Container

 

 

  ejb/UserManager

  Session

 

  com.jdon.security.auth.ejb.UserManagerLocalHome

 

  com.jdon.security.auth.ejb.UserManagerLocal

  UserManager

 

 

 

  ejb/RoleManager

  Session

 

  com.jdon.security.auth.ejb.RoleManagerLocalHome

 

  com.jdon.security.auth.ejb.RoleManagerLocal

  RoleManager

 

 

 

  ejb/AsyncSender

  Session

 

  com.jdon.asyncsender.ejb.AsyncSenderLocalHome

 

  com.jdon.asyncsender.ejb.AsyncSenderLocal

  AsyncSender

 

 

 

  User

  User

 

 

  在最后几行,指定角色名User指向了security-role中的User,两者名称可以不一样,但role-link必须和security-role是一致的。role-name可以是RegisterUser,那么在SecurityFacadeBean调用就是isCallerInRole("RegisterUse"),检查当前访问SecurityFacadeBean的角色是否是容器中已经定义的、允许访问的角色RegisterUse。

  通过ejb-jar.xml的配置,使得角色的访问权限的设置和管理变得非常方便。在系统运行时,有可能有新的角色加入或原有角色权限的修改。这些都可以通过ejb-jar.xml进行修改,修改完毕,运行中的J2EE系统立即生效。

  具体ejb-jar.xml的权限配置可参考Sur公司EJB 标准手册。

  为了使该EJB的权限机制激活,还需要在相应的容器配置文件中进行适当配置。如在JBoss中部署该EJB时,需要在jboss.xml加入:

  java:/jaas/SecurityRealm

  这表示该EJB的安全域将使用JAAS中的SecurityRealm配置。关于如何设置JBoss的SecurityRealm将在部署配置

<script type="text/javascript"><!-- google_ad_client = "pub-2947489232296736"; /* 728x15, 创建于 08-4-23MSDN */ google_ad_slot = "3624277373"; google_ad_width = 728; google_ad_height = 15; //--> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"><!-- google_ad_client = "pub-2947489232296736"; /* 160x600, 创建于 08-4-23MSDN */ google_ad_slot = "4367022601"; google_ad_width = 160; google_ad_height = 600; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
阅读更多
个人分类: ajax xml
想对作者说点什么? 我来说一句

基于容器用户安全管理系统

2009年01月14日 255KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭