背景技术
在J2EE应用领域,技术日新月异,各类开发框架层出不穷,版本更新迅速,不同框架、开源包、版本之间都可能存在不兼容性(比如Struts1、Struts2、webwork、JSF、Spring、Hibernate、Seam、Common工具包等等),不能放在同一war(Web Application Archive)系统中运行。然而软件公司开发自己的多个产品或者不同时期的产品以及将来要开发的产品所用到的框架、开源包、版本都会有所不同,这样开发出的产品是不兼容的,至少是不能通过简单的复制一下代码就达到复用的。这便造成了当客户购买了同一软件公司多个系统之后,他需要使用多套系统管理,建立多套用户,使用各个系统也要分别登录,这给用户的体验就非常的差了,需要一种方法可以让开发的产品即可以单独使用又可以集成使用。
当前市场上有很多的单点登录解决方案,但是这些方案都有各自的缺点,不能完美的解决上面的业务场景。
CAS方式:
CAS(Central Authentication Service)是一款不错的针对Web应用的单点登录框架,从结构上看,CAS包含两个部分:CAS Server和CAS Client。CAS Server需要独立部署,主要负责对用户的认证工作;CAS Client负责处理对客户端受保护资源的访问请求,需要登录时,重定向到CAS Server。
CAS Client与受保护的客户端应用部署在一起,以Filter方式保护受保护的资源。对于访问受保护资源的每个Web请求,CAS Client会分析该请求的Http请求中是否包含Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的CASServer登录地址,并传递Service(也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户输入认证信息,如果登录成功,CAS Server随机产生一个相当长度、唯一、不可伪造的Service Ticket,并缓存以待将来验证,之后系统自动重定向到Service所在地址,并为客户端浏览器设置一个Ticket Granted Cookie(TGC),CAS Client在拿到Service和新产生的Ticket过后,与CAS Server进行身份核实,以确保Service Ticket的合法性。
CAS的单点登录方式主要是解决了身份认证的登录问题,而登录以后的Session数据交换,却没有很好的解决。同时在具体系统整合的时候使用CAS方式也过于复杂,包括用户映射等。
简单模拟登录方式:
A、B两系统,当用户登录A以后需要打开B系统的链接时,把用户的信息做参数传递过去,B系统根据参数自动模拟登录一次。该方法实施起来很简单但是功能也很简单,不能解决Session数据交换的问题。同时把用户信息加到链接后面做参数安全性方面也缺乏保障。
发明内容
本发明主要解决使软件公司基于不同框架、开源包、版本开发出的产品可以方便的进行系统管理的整合与单点登录的问题。
在本发明中所说的系统表示一个war应用;一个产品也就是一个系统。
为解决该技术问题,本发明提供一种多系统的单点登录整合方法,包括以下步骤:
构建人员组织岗位以及他们之间关系的视图,把各个系统中人员表、组织表、岗位表中的共用字段提取到视图;
在所述视图中定义各个系统的查询数据接口以及实现;
将第二系统的菜单资源、需要控制权限的按钮、选项卡等资源添加到第一系统;
在应用服务器的公共jar目录中增加公共单列类,所述公共单列类中有3个键值对的Map对象用来存储数据;
在用户成功登录第一系统时向所述公共单列类中写入需要传递到第二系统的数据;
在第一系统的菜单脚本中,添加事件控制,当用户点击的第一系统中进入第二系统业务模块的菜单项时,自动在请求中添加当前登录用户在第一系统的SessionID;
用户点击第一系统菜单中进入第二系统业务模块的菜单项,请求被部署在第二系统中的登录过滤器拦截,所述登录过滤器自动登录第二系统,进入第二系统业务模块;
在第二系统登录时通过公共单列类把第一系统保存的用户在第二系统中的权限传递给了第二系统。
本发明通过应用服务器的系统类加载机制,利用公共jar包中类只加载一次的原理来实现数据交换和单点登录,并通过视图的方式隔离各个业务系统与系统管理模块的关系,使业务系统可以方便的整合到各种系统管理中。
具体实施方式
假设有A、B两个系统,需要把B系统集成到A系统,让用户登录了A系统以后便可以直接进入B系统各个业务模块使用,给用户的体验好像A系统跟B系统就是同一个系统一样。
各个业务系统之间有一些模块是非常相似的,其中最重要的一个就是系统管理模块。系统管理模块包括人员管理,组织管理,岗位管理,资源管理,权限管理等功能,它是整个系统其他业务模块的构建基础。每个系统虽然都有类似的系统管理模块,但是具体的实现代码有所不同,表名、字段名、细节的字段数量等等都有所差别。本发明方法第一步要解决的问题就是系统管理模块的整合,要让不同系统集成以后,用户只需使用一个系统管理就可以管理多个系统,只需创建一次人员组织岗位的基础数据就可以给多个系统共用,而且用户从一个系统入口就可以管理所有系统的权限。
第一步:数据库虽然有一些差别但是总体上是差不多的,首先构建一个人员组织岗位以及他们之间关系的视图,把各个系统中人员表、组织表、岗位表中的共用字段提取到视图,业务模块需要关联使用的系统管理模块的数据往往就在提取出的这些视图的公共字段中。其次在此视图上面定义各个系统同自己的查询数据的接口以及实现,每个系统的实现有所不同,A系统可能基于Hibernate而B系统基于JDBC,但是这些实现都是基于视图开发的,保证了此实现可以放到任何系统的数据库上运行。而业务模块与系统管理数据库的关联都通过这些接口来调用,不直接关联基于表的系统管理实现。这一步又保证了业务系统与系统管理的解耦。最后每个系统都有一个基于自己数据库表的系统管理实现,这保证各个系统是可以独立运行的。通过以上3点系统的业务模块就与系统管理模块从数据库层与代码层都解耦了,为下一步的操作做好了基础。
第二步:AB两系统业务模块界面上的整合。首先将B系统的菜单资源添加到A系统,这样便添加了A系统到B系统的入口,当然此时从A系统进入B系统的业务模块是不成功的,会提示没有登录,第三步将处理这个问题。其次将B系统需要控制权限的按钮、选项卡等也作为资源也添加到A系统,这样便可以在A系统的设置权限的页面同时设置B系统的权限,当然现在设置的权限是不起作用的,第四步讲处理这个问题。
在界面与数据库结构方面都整合以后,需要解决了就是系统登录与退出的问题。本发明方法是利用了java应用服务器的系统类机制来解决系统登录与退出的,这种方法要求各个系统部署在同一个应用服务器下面。首先了解一下java应用服务器的系统类加载顺序机制。一个在应用服务器中运行的系统加载一个类的顺序是这样的:
先从系统自身的web-inf/classes目录下搜索类,如果找到则加载类返回。
如果没有找到相应类则去系统自身的web-inf/lib目录下搜索类,如果找到则加载类返回。
如果没有找到相应类则去应用服务器中的公共jar目录中搜索类,如果找到则加载类返回。
如果没有找到相应类则去应用服务器中的上一级别公共jar目录中搜索类,如果找到则加载类返回。
如果没有找到相应类则去更上一级的公共jar目录搜索类,一直到最上级别的的公共jar目录。
如果还是没有找到相应类则去应用服务器运行的jvm的类目录下加载类。
不同的应用服务器拥有的公共jar目录的层级是不一样的,但至少都拥有一个公共jar目录。
例如附图2中所示的Tomcat5.5的类加载顺序。其中:
WebApp:载入WebApp根路径/WEB-INF/...,它们仅对该WEB APP可见。Shared:载入Tomcat根路径/shared/...,它们仅对所有WEB APP可见。Common:载入Tomcat根路径/common/...,它们对TOMCAT自身和所有的WEB APP都可见。
System:载入/*.class
JVM:载入JVM自带的类和/jre/lib/ext/*.jar。
其次除了类加载顺序的机制还有另外一个类加载次数的机制,放在公共jar目录的类是只会被加载一次的,类加载以后就会给该级别公共目录下面所有的系统共用,这样如果有一些类变量或者静态类或者单列类放在公共jar目录,则各个系统可以访问到相同对象。利用应用服务器的这些原理可以解决系统登录与退出的问题。
第三步:系统登录与退出问题的解决。
编写一个单列类(只会被实例化一次的类叫做单列类)。在单列类中有3个键值对的Map对象用来存储数据。把单列类放到应用服务器的公共jar目录中,这样A、B两系统都可以访问到单列类。第一个Map对象importMap:键存储的是用户在A系统中的SessionID,值存储的是A系统要传递给B系统的数据。第二个Map对象loadAgain:键存储的是用户在A系统中的SessionID,值存储的Boolean值表示A系统传递给B系统的数据更新了。第三个Map对象logoutMap:键存储的是用户在A系统中的SessionID,值存储的Boolean值表示用户退出了A系统或者A系统用户已经超时。
修改A系统的登录方法,添加如下功能:在用户成功登录A系统的时候往公共单列类中写入需要传递到B系统的数据(用户名密码、用户所属组织、所属岗位、操作权限等),也就是在公共单列类的importMap中存放用户在A系统中的SessionId与需传递数据的键值对。
在A系统的菜单JS中,对菜单点击事件添加控制,当用户点击的A系统中进入B系统业务模块的菜单项时,自动在请求中添加如下参数:SSOSessionId=当前登录用户在A系统的的SessionId。
用户点击A系统菜单中进入B系统业务模块的菜单项,请求被部署在B系统中的登录过滤器拦截,登录过滤器自动登录B系统,再进入具体的B系统业务模块。登陆过滤器的实现如下:
a.判断当前Session是否已经登录,如果没有登录进入步骤b,如果已经登录过则进入步骤e。
b.取得URL请求中传递的参数SSOSessionId,根据SSOSessionId去公共单列类中取得SSOSessionId在单列类中保存的A系统传递过来的数据,这些数据包括用用户名、密码、以及当前用户在B系统中的资源操作权限等,根据这些数据进行验证登录B系统,同时在B系统Session中保存当前用户在A、B两个系统中的SessionId对应关系。如果此步骤登录成功则进入步骤c。否则进入步骤d。
c.进入B系统的业务模块。
d.登录失败提示用户。
e.取得当前用户在B系统中的SessionId,通过此SessionId根据Session中保存的对应关系得到用户在A系统中的SessionId。根据用户在A系统的SessionId访问单列类取得logoutMap相关的值,如果为true。则表示当前用户在A系统中已经退出系统,则B应用也要退出系统,进入步骤f。否则进入步骤g。
f.用户退出B应用,销毁用户在B应用中的Session。
g.根据当前用户在A系统的SessionId访问单列类取得loadAgain相关的值,如果为true,表示A系统传递过来的数据已经更新,进入步骤h,否则进入步骤c。
h.重新取得单列类importMap中对应的数据,更新当前用户在B系统Session中的数据。进入步骤c。
在A系统中添加一个Session侦听器来解决系统退出的问题。侦听器功能实现如下:首先取得当前销毁的Session的SesssionId,将该SessionId在公共单列类中logoutMap对应的值设为true。同时模拟浏览器发送一个用户请求到B系统。B系统的自动退出实现见4中的步骤e。
具体流程参见附图1。
第四步:权限的整合
由于A系统保存了用户在B系统中的权限,同时在B系统自动登录的时候又通过公共单列类把权限数据传递给了B系统。在此基础上处理权限的方法实现如下:在有权限控制的标签或者其他有控制权限的代码中,判断权限的时候基于接口开发,接口的实现类有两个,一个是自身的实现,一个是通过前面传递过来的数据进行判断的实现。这样在系统整合的时候只需要替换一下接口实现便可以解决权限的整合。