




已阅读5页,还剩61页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
JMS基础示例教程需求分析在很多实际应用中,不只是简单地要求用户需要注册登录。还要求不同的用户对不同资源拥有不同的权限。某单位的新闻部门的某些人才拥有对外发布新闻的权限;每个部门只有对自己部门内部资源才拥有创建、修改权限,对于其他部门创建的资源如网页等只有浏览的权限。这些复杂的用户权限验证需要可以总结为如下几点: 用户资料管理系统:对用户的注册资料进行管理。可进行用户资料的新增修改,能够查询获取丢失的密码。 权限的验证和授权:拦截所有访问受保护资源的请求,检查该用户是否被授权访问该资源。 可控制的资源限制访问:对于系统的资源,无论是数据或行为动作,对确定的一组用户都有限制访问。例如对于有的用户只允许读操作,不能进行写操作;有些资源只有创建者才可以访问和操作等。这3种需求适合大部分应用系统,通过它们的实现,可以形成一个用户安全管理方面的组件框架,并且能够重复地在不同的应用系统中使用。 2 架构设计实现一个完整的用户安全管理框架,可以有两种实现途径。在J2EE出现以前,大部分是由应用系统本身实现。因此,很多有经验的软件商都拥有自己成熟的用户安全管理系统,但是缺点也是比较明显,自己设计的用户安全管理系统可重用性低,有的和具体应用程序过分紧密地绑定在一起,无法移植到其他系统上。但是,随着业务量上升,应用系统的不断增加,会出现不同应用系统拥有不同的用户登录验证体系,很显然,这给用户访问带来了不方便,用户不可能为每个系统注册一套用户和密码,定制一套用户角色。因此,整个服务器群中需要统一的用户权限验证体系。而J2EE容器的用户安全管理框架再辅助以LDAP或数据库系统,可以很方便地达到这个目标。2.1 角色J2EE容器的用户安全框架是基于RBAC(Roled-Based Access Control,相关网址:/rbac/)设计模型建立的,这是一个基于角色的访问权限控制模型。首先必须了解角色的含义,在RBAC中角色Role的定义是:Role是明确表达访问控制(Aceess Control)策略的一种语义构建词。角色可以是指做某些事情的资格,比如医生或物理学家;也可以包含权力和责任的意思,如部门经理或局长等。角色和组(groups)是有区别的。组就是纯粹代表一群用户;角色一方面代表一系列用户,另外一方面可以代表一系列权限,因此可以说是用户和权限的结合体。引入角色的概念主要是为了分离用户和访问权限的直接联系。用户与访问权限的直接组合可能是短暂的,而角色则可以相对稳定,因为一个系统中和角色相关的权限变化是有限的。在RBAC理论出现之前,很多人都是把用户和权限混淆在一起,这样当用户或权限发生变化时,都会涉及到对方,很显然这在实际实现中将是非常复杂的。所以诞生RBAC,创造了一个“角色”的名词,注意这是人为创造的语义词。角色就是用户和权限之间的第3者,通过引入角色概念,将用户和权限的关系解耦。这样用户的变化只要涉及到角色就可以,无需考虑权限。而权限的变化只涉及到角色,无需考虑用户或用户组。因此,基于角色的访问控制系统可以分为两个部分:与角色相关的访问权限系统以及与角色相关的用户管理系统。这样,通过角色这个中间者,将用户和权限联系在一起。其实这也非常符合日常生活的逻辑,例如王三来到某公司做业务员,公司章程规定了业务员一定的权限和职责,王三进入了业务员的角色,王三也就有了这些权限和职责,但这些权限职责不是和王三本人有直接联系的,而是通过王三的角色才会发生在王三身上;如果王三升迁做经理,表示其进入经理这样的角色,由此经理角色拥有的权限和职责王三又会拥有。由于有了这样两个分离的系统,因此在具体应用上可以分别实现,在J2EE中,与角色相关的访问权限是通过配置文件(Web.xml和ejb-jar.xml)由容器自动实现的,而且这种访问权限的配置也是非常方便灵活的。而与角色相关的用户系统则由具体应用系统的开发者来实现,可以采取基于数据库或LDAP等技术的数据系统来实现,例如用户注册资料的新增和修改等。本项目的设计思路就是完全按照这两种分离的思路实现的,将与角色相关的访问权限系统交由J2EE容器实现。因此,如何配置J2EE将是本项目实现中的一个主要部分;代码设计编程则主要集中在基于数据库的用户管理系统上。2.2 J2EE的JAASJ2EE容器实现了与角色相关的访问权限功能,如何在自己的具体应用系统开发中使用J2EE的安全系统?通过使用J2EE容器提供的JAAS (Java Authentication Authorization Service,Java验证和授权API),JAAS是J2EE服务器用来帮助应用系统实现安全功能的。当应用系统的开发者具体实现了LoginModule API,那么J2EE容器就执行LoginModule接口,通过接口和具体实现之间的关系,J2EE容器将结合具体应用系统实现特定的JAAS功能。标准的LoginModule接口可以让应用系统开发者自由地选择数据系统,如数据库、LDAP(lightweight directory access protocol)或者共享文件系统。这些变化却无需修改程序。这就形成了基于J2EE安全系统开发模式:编写一个Login Module,然后打包到自己的具体应用中,然后以一种约定的方式发布到J2EE平台上。图6-1显示在用户登录的情况下,应用系统实现的LoginModule与J2EE容器如何交互实现JAAS。图6-1 J2EE的Login交互图当用户实现LoginContext.login()时,Web容器将调用JAAS。JAAS确认应用系统自己实现的LoginModule正确配置后,用户和密码验证等验证工作交由应用系统自己的LoginModule实现。正常登录后,用户将通过Web容器访问业务逻辑核心的EJB容器,J2EE中可以对每个EJB的方法实现访问权限控制,这些都可以在ejb-jar.xml中配置。当Web容器发出一个EJB的lookup命令时,容器将从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 tSubject.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 单点登录单点登录(SSO,single sign on)是指在分布式环境下,整个系统只有一个可以登录进入的点,它对所有的请求(Request)都是通用的。单点登录可以保证用户能够访问到可以访问的资源,如果有一个未被授权的请求要求访问被保护的资源,这个请求将自动被导向到相应的验证点进行登录验证。J2EE容器支持单点登录模式的实现。通过使用J2EE Web层安全机制,可以保护Web层的一些资源如URL、URL模型以及HTTP的提交方式(POST或GET等)。当未被授权的用户访问这些受保护的资源时,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中需要如下语句:其中,Action的值、用户名以及密码名都必须分别严格采用j_security_check、j_username和j_password英文写法。除此之外可以任由开发人员发挥了。Login.jsp提交到j_security_check后,Web容器将执行,如图6-1所示的流程,如果验证成功,返回Web层的一个subject。登录类型的设置比较简单,在web.xml中加入如下语句: BASIC Register User 这表示是基于HTTP的基本验证,而使用下列语句表示基于表单的验证: FORM SecurityRealm /account/login.jsp /account/login_error.jsp 需要应用系统开发人员自己设置login的页面和出错页面,/account/login.jsp表示在路径account的login.jsp页面。2.4 邮件发送组件在本项目用户管理的需求中,需要对丢失的密码进行查询。输入用户的信箱,系统将查询后的密码发往用户的信箱。这将使用JavaMail连接专门的SMTP服务器进行信件发送,这个信件的发送过程有可能因为网络原因或其他未知原因导致处理时间延长,如果系统中的其他处理事务都要等待这个过程的完成,显然是没有效率而且问题会很多。采用JMS的邮件发送组件是基于一个异步消息机制的可重用系统,该组件系统采取session bean作为Queue的消息生产者,而MDB作为Queue的消息使用者,作为EJB的一个实现,可以重复使用在需要邮件发送功能的应用系统中,如图6-4所示。图6-4 邮件发送组件图客户端只要直接调用EJB AsyncSender就可以实现邮件发送功能。 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 =?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 roleManagerLo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 急性胰腺炎相关检查及治疗要点CT增强扫描测试题附答案
- 2025年安全专业考试试题及答案
- 潮阳初中模拟考试题及答案
- 2025年医学免疫考试试题及答案
- 雄县消防笔试题目及答案
- 2025年长沙学院考试真题及答案
- 化学探究与创新能力提升试题
- 化学反应的限度和速率初步试题
- 2025年高考物理选择题专项提速训练试题
- 汉川初中入学试卷及答案
- 最终版附件1:“跨学科主题学习”教学设计(2025年版)
- 中国血脂管理指南(基层版+2024年)解读
- 青海省历年中考语文现代文阅读真题33篇(截至2024年)
- 艾梅乙防治知识培训课件
- 2025年春新北师大版数学七年级下册课件 第四章 三角形 问题解决策略:特殊化
- 校外培训机构传染病防控健康教育制度
- 大学语文知到智慧树章节测试课后答案2024年秋南昌大学
- 【MOOC】声乐作品赏析与演唱-扬州大学 中国大学慕课MOOC答案
- 60岁以上务工免责协议书
- 【MOOC】机械之美-神奇的矿冶机械(双语)-江西理工大学 中国大学慕课MOOC答案
- T-IAC CAMRA 47.3- 2022汽车覆盖件低碳维修技术规范 第3部分:车辆玻璃
评论
0/150
提交评论