- 浏览: 103168 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
vampirehgg:
请教一下,登录成功跳转回客户端时,地址栏中会自动加上ticke ...
【原创】CAS总结之协议分析篇 -
Readiay:
【原创】CAS总结之单点退出篇(CAS到底有没有实现单点退出?) -
gyxkg:
很好,感谢楼主,做的漂亮
【原创】CAS总结之协议分析篇 -
dadafei007:
...
【原创】CAS总结之Ticket篇 -
zxmsdyz:
非常清楚 感谢楼主付出
【原创】CAS总结之协议分析篇
CAS 总结之单点退出篇
CAS 到底有没有实现单点退出?本人阅读了 JA-SIG CAS v3.3 ,以及 JA-SIG CAS-CLIENT 3.1.9 的源代码,发现表面上好像实现了单点退出,但实际上却没有真正实现。
现将 CAS 的 logout 接口的实现整理如下。
首先看一下 CAS logout 功能的序列图。
CAS logout
功能的序列图
从图中可以看出, CAS logout 功能有两步,一是调用 TGT 对象中各个 Service 的 logoutOfService 方法,二是在缓存中清除 TGT 对象。
我们看一下 CAS 的 AbstractWebApplicationService 中 logoutOfService 方法的实现。
public synchronized boolean logOutOfService(final String sessionIdentifier) { if (this.loggedOutAlready) { return true; } LOG.debug("Sending logout request for: " + getId()); final String logoutRequest = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"" + GENERATOR.getNewTicketId("LR") + "\" Version=\"2.0\" IssueInstant=\"" + SamlUtils.getCurrentDateAndTime() + "\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@</saml:NameID><samlp:SessionIndex>" + sessionIdentifier + "</samlp:SessionIndex></samlp:LogoutRequest>"; this.loggedOutAlready = true; if (this.httpClient != null) { return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest); } return false; }
另外 HttpClient 类中 sendMessageToEndPoint 方法的实现如下:
public boolean sendMessageToEndPoint(final String url, final String message) { HttpURLConnection connection = null; BufferedReader in = null; try { if (log.isDebugEnabled()) { log.debug("Attempting to access " + url); } final URL logoutUrl = new URL(url); final String output = "logoutRequest=" + URLEncoder.encode(message, "UTF-8"); connection = (HttpURLConnection) logoutUrl.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setReadTimeout(this.readTimeout); connection.setConnectTimeout(this.connectionTimeout); connection.setRequestProperty("Content-Length", "" + Integer.toString(output.getBytes().length)); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); final DataOutputStream printout = new DataOutputStream(connection .getOutputStream()); printout.writeBytes(output); printout.flush(); printout.close(); in = new BufferedReader(new InputStreamReader(connection .getInputStream())); while (in.readLine() != null) { // nothing to do } if (log.isDebugEnabled()) { log.debug("Finished sending message to" + url); } return true; } catch (final Exception e) { log.error(e,e); return false; } finally { if (in != null) { try { in.close(); } catch (final IOException e) { // can't do anything } } if (connection != null) { connection.disconnect(); } } }
通过阅读代码可以发现, logOutOfService 方法是调用 serivce 的 originUrl 接口,利用 HttpURLConnection 的方式把退出请求发送给 service ,注意没有给 HttpURLConnection 设置 requestMethod ,因此用的是默认的 GET 方法。 sessionIdentifier 的值是 ST 的值。 service 在 response 中会解析 logoutRequest 参数中的 sessionIdentifier 的值,然后把 sessionIdentifier 标识的 session kill 掉就可以了。这时我们发现,原理上是可以单点退出的。
再来看客户端的实现,客户端和单点退出有关的类包括:
- SingleSignOutFilter :用来解析 logoutRequest 参数。
- SessionMappingStorage :一个接口,定义了 Session 存储器的方法。
- HashMapBackedSessionMappingStorage : Session 存储器的实现类,定义了 2 个 Map 来存储 Session 。
MANAGED_SESSIONS:key 为 ST 的值, value 为 session ;
ID_TO_SESSION_KEY_MAPPING : key 为 sessionId,value 为 ST 的值。
-
SingleSignOutHttpSessionListener
:此
Listener
监听到
session
destroy
的事件后,用
sessionId
从上述
ID_TO_SESSION_KEY_MAPPING
中取出
ST
的值,然后依据
ST
的值从
MANAGED_SESSIONS
中取出
session,
然后就可以执行其
invalidate
方法了。
我们看一下 SingleSignOutFilter 中的 doFilter 方法。
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; if ("POST".equals(request.getMethod())) { final String logoutRequest = CommonUtils.safeGetParameter(request, "logoutRequest"); if (CommonUtils.isNotBlank(logoutRequest)) { if (log.isTraceEnabled()) { log.trace ("Logout request=[" + logoutRequest + "]"); } final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex"); if (CommonUtils.isNotBlank(sessionIdentifier)) { final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier); if (session != null) { String sessionID = session.getId(); if (log.isDebugEnabled()) { log.debug ("Invalidating session [" + sessionID + "] for ST [" + sessionIdentifier + "]"); } try { session.invalidate(); } catch (final IllegalStateException e) { log.debug(e,e); } } return; } } } else { final String artifact = CommonUtils.safeGetParameter(request, this.artifactParameterName); final HttpSession session = request.getSession(false); if (session != null) { if (log.isDebugEnabled()) { log.debug("Storing session identifier for " + session.getId()); } if (CommonUtils.isNotBlank(artifact)) { try { SESSION_MAPPING_STORAGE.removeBySessionById(session.getId()); } catch (final Exception e) { // ignore if the session is already marked as invalid. Nothing we can do! } SESSION_MAPPING_STORAGE.addSessionById(artifact, session); } } else { log.debug("No Session Found, so ignoring."); } } filterChain.doFilter(servletRequest, servletResponse); }
非常奇怪,这个方法首先判断了 request 的方法,如果是 POST, 则会执行 session.invalidate 方法,从而实现单点退出,如果是 GET ,则只会在存在 ticket 参数的情况下,把 session 存进 SessionMappingStorage ,永远也不执行 session.invalidate 方法,不能单点退出。因为 CAS 的 logoutRequest 请求是用 GET 方法发过来的,所以,单点登录功能没有实现。
本人对 SingleSignOutFilter 中的 doFilter 方法重写了一下,代码如下。经验证,确实实现了单点退出。
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final String logoutRequest = CommonUtils.safeGetParameter(request, "logoutRequest"); Enumeration ff = request.getParameterNames(); String a = request.getQueryString(); if (CommonUtils.isNotBlank(logoutRequest)) { final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex"); if (CommonUtils.isNotBlank(sessionIdentifier)) { final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier); if (session != null) { String sessionID = session.getId(); try { session.invalidate(); } catch (final IllegalStateException e) { } } } } else{ final String artifact = CommonUtils.safeGetParameter(request, this.artifactParameterName); final HttpSession session = request.getSession(false); if (CommonUtils.isNotBlank(artifact) && session!=null) { try { SESSION_MAPPING_STORAGE.removeBySessionById(session.getId()); } catch (final Exception e) { } SESSION_MAPPING_STORAGE.addSessionById(artifact, session); } } filterChain.doFilter(servletRequest, servletResponse); }
另外还需要注意的是,因为客户端部署了三个 Filter:AuthenticationFilter 、 ServiceValidationFilter 、 SingleSignOutFilter ,所以三个 Filter 的顺序需要注意,我的顺序为 AuthenticationFilter 、 ServiceValidationFilter 、 SingleSignOutFilter ,一开始不行,因为执行退出功能时, CAS 服务端用 HttpURLConnection 访问客户端,没有把 sessionId 代过来,所以在 AuthenticationFilter 中就被 redirect 回 CAS 了,到不了 SingleSignOutFilter ,我做了一个改动,就是在 AuthenticationFilter 中的 redirectUrl ,后面加上了 session ID 的值,格式如:“ ;jsessionid= ” , 这样 CAS 端解析 service 参数生成 WebApplicationService 时, orginUrl 里就有 sessionId 了。
虽然这样就实现了单点退出,但我感觉 CAS 的这种采用 Filter 的方式太麻烦了,不如让客户应用提供一个 callback url ,CAS 直接调用这个 callback url 来退出更好一些,但这样的话,对 CAS 的改动非常大。
本人博客 :http://zhenkm0507.iteye.com
评论
一个简单的方法: 各应用提供一个url ,访问这个url就会把当前域下 cookie 都设置过期,并把当前session 失效掉。
这样cas只需要生成一个页面,页面中分别get请求各个系统的这个url就可以了,可以生成多个iframe并指定其src 来实现。
这方法和cas的机制没多大关系,比较简单,而且在业务系统有集群的情况下也可以工作良好。
可以实现单点登出
是在DoFilter方法中加入这句话么? 能具体点么?
可以实现单点登出
默认配置是sign out的放在最前面的,因为这样就可以避免被Authentication Filter拦截了。
不过我用了一个笨方法。。。
自己写了个filter,当访问项目的/logout地址的时候直接清除Cookie和Session。
不过这样项目多了就郁闷了。。。
还是期待CAS4吧,我在他们的SVN上看到应该差不多完成了。
URL logoutUrl = new URL(url); String output = (new StringBuilder()).append("logoutRequest=").append(URLEncoder.encode(message, "UTF-8")).toString(); connection = (HttpURLConnection)logoutUrl.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.setReadTimeout(readTimeout); connection.setConnectTimeout(connectionTimeout); connection.setRequestProperty("Content-Length", Integer.toString(output.getBytes().length)); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); DataOutputStream printout = new DataOutputStream(connection.getOutputStream()); printout.writeBytes(output); printout.flush(); printout.close();
<!-- CAS 登出-->
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/logout</url-pattern>
</filter-mapping>
这样配置拦截器 无效 是什么原因?
顺序都搞错了。。SingleSignOutFilter 要放在最前面
CAS的论坛上倒是在说CAS4.0出来以后要实现真正的单点退出,解决这种集群里面的问题,不光是考虑CAS认证中心的集群,还要考虑到各个CLIENT也可能是个集群。不过这个可能就不知道啥时候去了,我一直关心CAS的站点,都没有看到要出4.0.。。。。
发表评论
-
【原创】CAS总结之集群环境篇
2010-01-08 21:54 8591CAS的集群环 ... -
【原创】CAS总结之协议分析篇
2009-12-11 23:34 9333请见附件。附件中的动画演示了CAS1.0协议及2.0协议中Cl ... -
【原创】CAS总结之Ticket篇
2009-12-11 23:20 34688CAS的核心就是其Ticket,及其在Ticket之上的一系列 ... -
CAS总结之单点退出篇
2009-12-11 22:18 3107请见本人另一篇文章 CAS总结之单点退出篇(CAS到底有没有 ... -
【原创】CAS调研总结
2009-12-09 16:55 11085本篇文章是对JA-SIG CAS( ...
相关推荐
在这个例子中,我们基于Spring MVC、Maven、WebService和Memcached来实现一个功能完善的单点登录系统,同时支持完全跨域和单点退出。 1. **Spring MVC**: Spring MVC是Spring框架的一个模块,主要用于构建Web应用...
Spring Boot 整合 CAS Client 实现单点登录验证的示例 Spring Boot 整合 CAS Client 是一种流行的解决方案,用于实现单点登录(Single Sign-On,简称 SSO)。在多个应用系统中,用户只需要登录一次就可以访问所有...
CAS单点登录,退出后ticket失效报出异常解决办法——换jar包 把客户端的 casclient.jar 包换成我的这个。
总结,CAS 作为一个强大的单点登录解决方案,为企业级应用提供了高效的身份验证管理。通过理解 CAS 的核心原理和实践步骤,开发者可以轻松地在 Java 应用中集成 CAS,提升系统的安全性与用户体验。
总之,CAS单点登录系统为多应用环境提供了一种高效的身份验证解决方案。通过Java和PHP客户端的集成,可以在多种技术栈的项目中实现一致的用户体验,同时也简化了用户管理和权限控制。在实际应用中,应根据项目需求...
使用struts2+spring+cas实现的单点登录功能,里面包括cas-server3.5.2项目一个,cas-client3.2.1 web项目两个,数据库脚本,请按照里面的说明文档进行部署,希望你们也能配置成功。
CAS单点登录,退出后ticket失效报出异常解决办法——换jar包 把客户端的 casclient.jar 包换成我的这个。
CAS(Central Authentication Service)是Java开发的一个开源的单点登录(Single Sign-On,简称SSO)框架,主要用于解决网络应用中的身份验证问题。本压缩包提供了CAS服务端自定义认证的实现,以及CAS客户端的配置...
* 单点退出监听器:org.jasig.cas.client.session.SingleSignOutHttpSessionListener * 单点登出过滤器:org.jasig.cas.client.session.SingleSignOutFilter * 认证过滤器:org.jasig.cas.client.authentication....
在本文中,我们将深入探讨CAS单点登录的基本原理、工作流程以及如何通过提供的Demo进行实践操作。 **CAS基本原理** CAS的核心思想是用户只需在一个应用系统中验证身份,之后访问其他所有支持CAS的应用系统时都不再...
### CAS单点登录框架 #### 一、CAS简介 单点登录(Single Sign On,简称SSO)是一种用户登录管理方式,用户只需要在一处登录,即可访问多个应用系统。这种方式为用户和系统管理员提供了便利,增强了用户体验,简化...
在实际应用中,单点退出(Single Sign-Out,简称SSO)也是重要的一环。当用户在一个应用中注销时,SSO机制会通知其他所有关联应用,同步注销状态,确保用户在整个系统中的会话都被终止。 总结起来,这个"单点登录...
此文档自己亲手从0开始一步一步配置的详尽过程,其中包括keytool创建 、ticket、tomcat配置cas、自定义登录页面,处理服务器返回的乱码,服务退出、cas服务器返回多数据等等文档 包括SSO原理图,以及认证流程图等
X2.5与CAS(Central Authentication Service)集成是一种常见的身份验证解决方案,它能够实现单点登录(Single Sign-On, SSO)功能。在SSO系统中,用户只需登录一次,就可以在多个相互独立的应用系统之间自由切换,...
### 使用CAS在Tomcat中实现单点登录的关键知识点 #### 一、CAS简介与特性 - **CAS**(Central Authentication Service)是由耶鲁大学发起的一个开源项目,它为Web应用程序提供了一种简单可靠且功能强大的单点登录...
CAS(Central Authentication Service...总之,CAS单点登录是提高用户体验和安全管理的重要工具,其核心在于集中化的身份验证和票证管理。理解和掌握CAS的工作原理及实施要点,有助于在实际项目中有效地应用这一技术。
CAS(Central Authentication Service)是一种开源的身份验证协议,提供了单点登录和单点退出功能。CAS Server 是一个独立的服务器,负责处理身份验证请求。 Spring Boot 集成 Shiro+Cas 要在 Spring Boot 应用...
在实现CAS单点登录的过程中,主要涉及以下几个核心知识点: 1. **CAS服务器**:CAS服务器是整个单点登录的核心,它负责处理用户的登录请求,验证用户的凭证,并生成服务票证(Ticket Granting Ticket,TGT)。一旦...
CAS(Central Authentication Service...总的来说,这些文档提供了全面的指导,帮助开发者在Linux上的Tomcat环境中实现CAS单点登录,涵盖了从基础理论到具体实践的各个层面,对于理解SSO机制和实际操作具有很大的价值。