Jboss3.2.3实现的SSO是基于客户端cookie和服务器端session的工作的。 基本工作流程是: 在${JBOSS_HOME}/server/default/deploy/jbossweb-tomcat41.sar/META-INF/jboss-service.xml中配置一个SSO的Valve,在待使用SSO的虚拟主机下加入如下代码: 启动Jboss时,Jboss会加载该Valve,创建一个org.jboss.web.tomcat.tc4.authenticator.SingleSignOn的实例(以下简称该实例为ourSSO)。 ourSSO实例会维护两个HashMap,分别为session与ssoId,ssoId与SingleSignOnEntry的映射集合。注:SingleSignOnEntry存放用户登录信息。 SSO处理单元:当访问被保护资源的请求从客户端传来时,ourSSO会首先对它进行处理,如果该用户已通过本应用验证,则直接将请求传入下一处理单元(安全验证);如果用户未通过本应用验证,则读取请求对象中的cookie,查找是否存在Jboss的SSO标志cookie,如果有,读出cookie中的ssoId(SingleSignOnEntry的标志Id),根据ssoId在存放SingleSignOnEntry的HashMap中查找对应的用户信息,如果找不到对应的SingleSignOnEntry,通过HttpServletResponse清除客户端对应cookie,如果可以找到,将ssoId放入一个全局的标志变量中,并将请求传入下一处理单元,如果在请求对象中的cookie中查找不到JBoss的SSO标志cookie,则直接将请求传入下一处理单元。 安全验证单元:判断是否存在ourSSO实例,如果不存在ourSSO实例,则按照正常流程检查请求是否包含正确并拥有足够权限的用户信息;如果存在ourSSO实例,在验证完成后,检查ssoId是否存在并有效,如果ssoId存在并有效,调用SingleSignOn的方法更新ourSSO维护的两个HashMap表,如果ssoId不存在或无效,随机生成一个ssoId,并通过HttpServletResponse将此ssoId写入客户端cookie,同时向ourSSO维护的两个HashMap实例中分别加入新的映射。
ourSSO会一直监听session事件,如果某个session失效,ourSSO维护的两个HaspMap中的相关的映射会被删除掉。
可以看到,Jboss3.2.3实现的SSO是依赖于cookie的,如果客户端浏览器禁止cookie或不支持cookie,SSO将不能正常工作。
4. 基于URL重写实现的SSO
分析Jboss3.2.3实现的SSO原理,不难发现,SSO之所于依赖于cookie的关键在于,在服务器与客户端的交互过程中,将一个ssoId存放在了cookie中,所以只要能将ssoId通过另外的方式存放并传递,就可以实现不依赖与cookie的SSO,而URL重写就提供了这种方式。 URL重写是指把一个唯一的ID(由服务器生成)嵌入到所有的URL中,当客户点击这些重写后的URL发送请求至服务器时,服务器就可以读取这个ID,在服务器端查找到对应的session或其他一些客户信息。基于URL重写实现SSO,就是把一个唯一的ssoId嵌入到所有的URL中,而服务器端接受到包含ssoId的请求时,ourSSO就可以读取这个ssoId,查找对应的SingleSignOnEntry,然后进行验证等上述处理流程。
具体设计为: 写一个新类继承org.jboss.web.tomcat.tc4.authenticator.SingleSignOn,修改获取ssoId及清除ssoId的操作实现,由读取cookie和清除cookie,改为读取请求对象的一个标志Parameter和清除请求对象的一个标志Attribute,该标志Parameter用来从客户端传递ssoId到服务器端,而标志Attribute用来从SSO单元传递ssoId到服务器(因为Parameter无法清楚,而Attribute可以清除)。而安全处理单元中的写入cookie的操作,也对应的修改为添加标志Attribute到request中,然后在jsp或sevlet中将ssoId嵌入到URL中。 改变后的工作流程如下:SSO处理单元:当访问被保护资源的请求从客户端传来时,ourSSO会首先对它进行处理,如果该用户已通过本应用验证,则直接将请求传入下一处理单元(安全验证);如果用户未通过本应用验证,则查找请求中是否存在JBoss的SSO标志Paramter,如果有,读取ssoId(SingleSignOnEntry的标志Id),根据ssoId在存放SingleSignOnEntry的HashMap中查找对应的用户信息,如果找不到对应的SingleSignOnEntry,清除请求对象的标志Attribute,如果可以找到,将ssoId作为标志Attribute写入请求对象,同时将ssoId放入一个全局的标志变量中,并将请求传入下一处理单元,如果在请求对象中查找不到JBoss的SSO标志Paramter,则直接将请求传入下一处理单元。 安全验证单元:判断是否存在ourSSO实例,如果不存在ourSSO实例,则按照正常流程检查请求是否包含正确并拥有足够权限的用户信息;如果存在ourSSO实例,检查该实例是否是上面新写的SingleSignOn子类的实例,如果不是,则按照原流程进行处理,如果是,则按照如下流程进行处理:在验证完成后,检查ssoId是否存在并有效,如果ssoId存在并有效,调用SingleSignOn的方法更新ourSSO维护的两个HashMap表,如果ssoId不存在或无效,随机生成一个ssoId,作为标志Attribute写入请求对象,同时向ourSSO维护的两个HashMap实例中分别加入新的映射。 页面处理:通过读取请求对象的标志Attribute,获得ssoId,将其嵌入到URL中。其中需要注意下面两点:
(1) 对于form表单,如果以get方式提交,应将ssoId作为表单的一个input域方式嵌入;如果以post方式提交,可以将ssoId作为表单的一个input域方式嵌入,也可以在重写action的值,将ssoId嵌入进去。
(2) 重写URL或action值时,应注意当URL或action无参数,则附加的字符串应该以"?"开始,如已有参数,则应以"&"开始。 另外,对于服务器内部转发的请求,无法对这样的请求对象附加Parameter,所以对转发的请求应特殊处理。根据Sevlet2.3规范,转发的请求应在同一应用内,故在SSO处理单元,应在查找请求中是否存在JBoss的SSO标志Paramter不得之后,检查一下该请求的session是否在ourSSO维护的存放session-ssoId的HashMap中存在,如果存在,则将其对应ssoId值作为该请求的ssoId进行处理。 如上实现后,在${JBOSS_HOME}/server/default/deploy/jbossweb-tomcat41.sar/META-INF/jboss-service.xml中加入新写的SingleSignOn子类作为待实现SSO的虚拟主机的一个Valve,在浏览器关闭cookie的情况下,基于BASIC验证方式的应用间可以实现SSO的效果。因为JBoss3.2.3基于FORM安全域验证本身就对cookie存在依赖,所以无法验证上述实现后基于FORM的安全验证是否可以实现SSO的效果。