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

 

需求分析

在很多实际应用中,不只是简单地要求用户需要注册登录。还要求不同的用户对不同资源拥有不同的权限。某单位的新闻部门的某些人才拥有对外发布新闻的权限;每个部门只有对自己部门内部资源才拥有创建、修改权限,对于其他部门创建的资源如网页等只有浏览的权限。这些复杂的用户权限验证需要可以总结为如下几点:

·          用户资料管理系统:对用户的注册资料进行管理。可进行用户资料的新增修改,能够查询获取丢失的密码。

·          权限的验证和授权:拦截所有访问受保护资源的请求,检查该用户是否被授权访问该资源。

·          可控制的资源限制访问:对于系统的资源,无论是数据或行为动作,对确定的一组用户都有限制访问。例如对于有的用户只允许读操作,不能进行写操作;有些资源只有创建者才可以访问和操作等。

3种需求适合大部分应用系统,通过它们的实现,可以形成一个用户安全管理方面的组件框架,并且能够重复地在不同的应用系统中使用。

架构设计

实现一个完整的用户安全管理框架,可以有两种实现途径。在J2EE出现以前,大部分是由应用系统本身实现。因此,很多有经验的软件商都拥有自己成熟的用户安全管理系统,但是缺点也是比较明显,自己设计的用户安全管理系统可重用性低,有的和具体应用程序过分紧密地绑定在一起,无法移植到其他系统上。

但是,随着业务量上升,应用系统的不断增加,会出现不同应用系统拥有不同的用户登录验证体系,很显然,这给用户访问带来了不方便,用户不可能为每个系统注册一套用户和密码,定制一套用户角色。因此,整个服务器群中需要统一的用户权限验证体系。而J2EE容器的用户安全管理框架再辅助以LDAP或数据库系统,可以很方便地达到这个目标。


2.1  角色

J2EE容器的用户安全框架是基于RBACRoled-Based Access Control,相关网址:http://csrc.nist.gov/rbac/)设计模型建立的,这是一个基于角色的访问权限控制模型。

首先必须了解角色的含义,在RBAC中角色Role的定义是:Role是明确表达访问控制(Aceess Control)策略的一种语义构建词。

角色可以是指做某些事情的资格,比如医生或物理学家;也可以包含权力和责任的意思,如部门经理或局长等。角色和组(groups)是有区别的。组就是纯粹代表一群用户;角色一方面代表一系列用户,另外一方面可以代表一系列权限,因此可以说是用户和权限的结合体。

引入角色的概念主要是为了分离用户和访问权限的直接联系。用户与访问权限的直接组合可能是短暂的,而角色则可以相对稳定,因为一个系统中和角色相关的权限变化是有限的。

RBAC理论出现之前,很多人都是把用户和权限混淆在一起,这样当用户或权限发生变化时,都会涉及到对方,很显然这在实际实现中将是非常复杂的。所以诞生RBAC,创造了一个“角色”的名词,注意这是人为创造的语义词。角色就是用户和权限之间的第3者,通过引入角色概念,将用户和权限的关系解耦。这样用户的变化只要涉及到角色就可以,无需考虑权限。而权限的变化只涉及到角色,无需考虑用户或用户组。

因此,基于角色的访问控制系统可以分为两个部分:与角色相关的访问权限系统以及与角色相关的用户管理系统。这样,通过角色这个中间者,将用户和权限联系在一起。其实这也非常符合日常生活的逻辑,例如王三来到某公司做业务员,公司章程规定了业务员一定的权限和职责,王三进入了业务员的角色,王三也就有了这些权限和职责,但这些权限职责不是和王三本人有直接联系的,而是通过王三的角色才会发生在王三身上;如果王三升迁做经理,表示其进入经理这样的角色,由此经理角色拥有的权限和职责王三又会拥有。

由于有了这样两个分离的系统,因此在具体应用上可以分别实现,在J2EE中,与角色相关的访问权限是通过配置文件(Web.xmlejb-jar.xml)由容器自动实现的,而且这种访问权限的配置也是非常方便灵活的。

而与角色相关的用户系统则由具体应用系统的开发者来实现,可以采取基于数据库或LDAP等技术的数据系统来实现,例如用户注册资料的新增和修改等。

本项目的设计思路就是完全按照这两种分离的思路实现的,将与角色相关的访问权限系统交由J2EE容器实现。因此,如何配置J2EE将是本项目实现中的一个主要部分;代码设计编程则主要集中在基于数据库的用户管理系统上。

2.2  J2EEJAAS

J2EE容器实现了与角色相关的访问权限功能,如何在自己的具体应用系统开发中使用J2EE的安全系统?

通过使用J2EE容器提供的JAAS Java Authentication Authorization ServiceJava验证和授权API),JAASJ2EE服务器用来帮助应用系统实现安全功能的。当应用系统的开发者具体实现了LoginModule API,那么J2EE容器就执行LoginModule接口,通过接口和具体实现之间的关系,J2EE容器将结合具体应用系统实现特定的JAAS功能。

标准的LoginModule接口可以让应用系统开发者自由地选择数据系统,如数据库、LDAPlightweight directory access protocol)或者共享文件系统。这些变化却无需修改程序。

这就形成了基于J2EE安全系统开发模式:编写一个Login Module,然后打包到自己的具体应用中,然后以一种约定的方式发布到J2EE平台上。

6-1显示在用户登录的情况下,应用系统实现的LoginModuleJ2EE容器如何交互实现JAAS

6-1  J2EELogin交互图

当用户实现LoginContext.login()时,Web容器将调用JAASJAAS确认应用系统自己实现的LoginModule正确配置后,用户和密码验证等验证工作交由应用系统自己的LoginModule实现。

正常登录后,用户将通过Web容器访问业务逻辑核心的EJB容器,J2EE中可以对每个EJB的方法实现访问权限控制,这些都可以在ejb-jar.xml中配置。当Web容器发出一个EJBlookup命令时,容器将从subject中获得角色。然后和ejb-jar.xml配置中相应EJB允许操作的角色相比较,如果两者一致,则允许lookup命令执行,EJB层将返回lookup后的bean

在本项目中,由于采取JBoss作为J2EE容器,JBoss提供了绑定的LoginModule,如LdapLoginModule DatabaseServerLoginModul,因此本项目就不必再编写LoginModule,只要在Jboss的配置文件中配置相应的LoginModule就可以。

关于LoginContext的实现有多种途径。

第一种是应用系统自己通过调用LoginContext.login()实现拦截验证功能,在拦截功能的模块中包含如下语句:

AppCallbackHandler handler = new AppCallbackHandler(name, passwordChar);

LoginContext lc = new LoginContext(Constants.LOGIN_MODULE, handler);

lc.login();

Subject subject = lc.getSubject();

PrivilegedAction action = new PrivilegedAction () {

public Object run() {

     // do something

     }

};

// perform action as Subject t

Subject.doAs(subject, action);

拦截功能的实现有几种,使用Servlet 2.3以上版本支持的Filter是比较好的办法,还需要重载HTTPServletRequestWrapper的某些方法。

这样做的好处是可以在用户登录后加入一些应用系统需要实现的预先工作,定制性很强,甚至直接在应用系统中使用自己的LoginModule,但是这些工作实现起来比较麻烦。

另外一种则比较简单,只要在Web按照Servlet 2.3的安全章节所规定的,在Web.xml配置login-config,由容器自动生成LoginContext并保存返回的Subject。本项目将采取该方案,关于login-config的配置和J2EE登录配置见2.3节。

2.3  单点登录

单点登录SSOsingle sign on是指在分布式环境下整个系统只有一个可以登录进入的点它对所有的请求Request都是通用的。单点登录可以保证用户能够访问到可以访问的资源,如果有一个未被授权的请求要求访问被保护的资源,这个请求将自动被导向到相应的验证点进行登录验证。

J2EE容器支持单点登录模式的实现。通过使用J2EE Web层安全机制,可以保护Web层的一些资源如URLURL模型以及HTTP的提交方式(POSTGET等)。当未被授权的用户访问这些受保护的资源时,J2EE容器会自动将用户导向到规定的登录界面,要求用户输入用户名和密码。

J2EE容器支持常见的下列几种登录验证机制。

·          基于HTTP的基本验证(HTTP Based Authentication):这是基于HTTP/1.0规定中的用户名和密码验证机制。类似以前在Apache中的设置,该方式的缺点是不够安全,密码只是简单地使用base64编码。当用户访问被保护资源时,浏览器跳出一个提示框,要求输入用户名和密码,如图6-2所示。

·          基于HTTPS的客户端验证(HTTPS Client Authentication0):客户端通过HTTPS HTTP over SSL)和服务器端发生互动,这个机制需要公用密钥证书,因此要安全得多。

·          基于表单验证(form-based authentication):登录界面可以使用JSP/HTML特定定制,因此在界面上可以更加美观,如图6-3所示。

      

6-2  基于HTTP/1.0的登录界面                     6-3  登录表单

但是对登录界面表单有限制。例如login.jsp是用户登录的界面,那么在login.jsp中需要如下语句:

<form method="POST" action="j_security_check">

<input type="text" name="j_username">

<input type="password" name="j_password">

</form>

其中,Action的值、用户名以及密码名都必须分别严格采用j_security_checkj_usernamej_password英文写法。除此之外可以任由开发人员发挥了。

Login.jsp提交到j_security_check后,Web容器将执行,如图6-1所示的流程,如果验证成功,返回Web层的一个subject

登录类型的设置比较简单,在web.xml中加入如下语句:

  <login-config>

    <auth-method>BASIC</auth-method>

    <realm-name> Register User </realm-name>

  </login-config>

这表示是基于HTTP的基本验证,而使用下列语句表示基于表单的验证:

<login-config>

    <auth-method>FORM</auth-method>

    <realm-name>SecurityRealm</realm-name>

    <form-login-config>

      <form-login-page>/account/login.jsp</form-login-page>

      <form-error-page>/account/login_error.jsp</form-error-page>

    </form-login-config>

 </login-config>

需要应用系统开发人员自己设置login的页面和出错页面,/account/login.jsp表示在路径accountlogin.jsp页面。

2.4  邮件发送组件

在本项目用户管理的需求中,需要对丢失的密码进行查询。输入用户的信箱,系统将查询后的密码发往用户的信箱。这将使用JavaMail连接专门的SMTP服务器进行信件发送,这个信件的发送过程有可能因为网络原因或其他未知原因导致处理时间延长,如果系统中的其他处理事务都要等待这个过程的完成,显然是没有效率而且问题会很多。

采用JMS的邮件发送组件是基于一个异步消息机制的可重用系统,该组件系统采取session bean作为Queue的消息生产者,而MDB作为Queue的消息使用者,作为EJB的一个实现,可以重复使用在需要邮件发送功能的应用系统中,如图6-4所示。

6-4  邮件发送组件图

客户端只要直接调用EJB AsyncSender就可以实现邮件发送功能。

 

详细设计和实现

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以上容器中,可以使用实体BeanCMR来表示一对多、多对一或多对多的关系。这样就无需写很多代码,但是使用数据表来表示关系更容易把握,更方便理解和使用。

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;                                             #使用MySQLInnoDB

以下是角色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为:

<entity>

  <display-name>User</display-name>

  <ejb-name>User</ejb-name>

  <local-home>com.jdon.security.auth.ejb.UserHome</local-home>

  <local>com.jdon.security.auth.ejb.UserLocal</local>

  <ejb-class>com.jdon.security.auth.ejb.UserBean</ejb-class>

  <persistence-type>Container</persistence-type>

  <prim-key-class>java.lang.String</prim-key-class>

  <reentrant>False</reentrant>

  <cmp-version>2.x</cmp-version>

  <abstract-schema-name>User</abstract-schema-name>

  <cmp-field>

     <field-name>userId</field-name>

  </cmp-field>

  <cmp-field>

     <field-name>password</field-name>

  </cmp-field>

  <cmp-field>

     <field-name>name</field-name>

  </cmp-field>

  <cmp-field>

     <field-name>email</field-name>

  </cmp-field>

     <primkey-field>userId</primkey-field>

  <query>

     <query-method>

     <method-name>findByEmail</method-name>

     <method-params>

        <method-param>java.lang.String</method-param>

     </method-params>

     </query-method>

      <ejb-ql>SELECT OBJECT(s) FROM User AS s WHERE s.email=?1</ejb-ql>

  </query>

  <query>

      <query-method>

          <method-name>findByName</method-name>

          <method-params>

          <method-param>java.lang.String</method-param>

          </method-params>

        </query-method>

        <ejb-ql>SELECT OBJECT(s) FROM User AS s WHERE s.name=?1</ejb-ql>

  </query>

</entity>

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

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

3.4  Session Bean实现

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

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;

  }

  //获得FacadeRoleManagerLocal

  public RoleManagerLocal getRoleManager() {

    RoleManagerLocal roleManagerLocal = null;

    try {

      roleManagerLocal = roleManagerLocalHome.create();

    } catch (Exception ex) {

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

    }

    return roleManagerLocal;

  }

  //获得当前sessionUser实例

  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是继承接口框架系统中的EJBControllerSecurityFacadeBean作为一个总的Facade类,通过接口框架负责和Web实现联系,如图6-6所示。

6-6  EJB Facade

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


3.5  EJB容器安全配置

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

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

<assembly-descriptor>

<security-role>

     <description>the role is super user </description>

      <role-name>Admin</role-name>

</security-role>

<security-role>

     <description>register user</description>

     <role-name>User</role-name>

</security-role>

<method-permission>

     <role-name>User</role-name>

     <method>

          <ejb-name>RoleManager</ejb-name>

           <method-name>*</method-name>

      </method>

</method-permission>

</assembly-descriptor>

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

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

<session>

            <display-name>SecurityFacade</display-name>

            <ejb-name>SecurityFacade</ejb-name>

            <local-home>

com.jdon.security.auth.ejb.SecurityFacadeLocalHome

</local-home>

            <local>com.jdon.security.auth.ejb.SecurityFacadeLocal</local>

            <ejb-class>com.jdon.security.auth.ejb.SecurityFacadeBean</ejb-class>

            <session-type>Stateful</session-type>

            <transaction-type>Container</transaction-type>

            <ejb-local-ref>

                <description />

                <ejb-ref-name>ejb/UserManager</ejb-ref-name>

                <ejb-ref-type>Session</ejb-ref-type>

                <local-home>

                   com.jdon.security.auth.ejb.UserManagerLocalHome

                </local-home>

                <local>com.jdon.security.auth.ejb.UserManagerLocal</local>

                <ejb-link>UserManager</ejb-link>

            </ejb-local-ref>

            <ejb-local-ref>

                <description />

                <ejb-ref-name>ejb/RoleManager</ejb-ref-name>

                <ejb-ref-type>Session</ejb-ref-type>

                <local-home>

                    com.jdon.security.auth.ejb.RoleManagerLocalHome

                </local-home>

                <local>com.jdon.security.auth.ejb.RoleManagerLocal</local>

                <ejb-link>RoleManager</ejb-link>

            </ejb-local-ref>

            <ejb-local-ref>

                <description />

                <ejb-ref-name>ejb/AsyncSender</ejb-ref-name>

                <ejb-ref-type>Session</ejb-ref-type>

                <local-home>

                   com.jdon.asyncsender.ejb.AsyncSenderLocalHome

                 </local-home>

                <local>com.jdon.asyncsender.ejb.AsyncSenderLocal</local>

                <ejb-link>AsyncSender</ejb-link>

            </ejb-local-ref>

            <security-role-ref>

                <description />

                <role-name>User</role-name>

                <role-link>User</role-link>

            </security-role-ref>

</session>

在最后几行,指定角色名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加入:

<security-domain>java:/jaas/SecurityRealm</security-domain>

这表示该EJB的安全域将使用JAAS中的SecurityRealm配置。关于如何设置JBossSecurityRealm将在部署配置相关章节中讨论。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值