SSO CAS原理剖析

       SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。

       CAS(Central Authentication Service)是Yale大学发起的一个企业级的、开源的项目,旨在为Web应用系统提供一种可靠的单点登录解决方法(属于WebSSO)。CAS开始于2001年,并在2004年12月正式成为JA-SIG的一个项目。

       SSO 实现模式千奇百怪,但万变不离其宗:

       1. Web应用不处理User的登录,否则就是多点登陆了,所有的登录都在SSO认证中心进行。

       2. SSO认证中心通过一些方法来告诉Web应用当前访问用户究竟是不是张三/李四。

       3. SSO认证中心和所有的Web应用建立一种信任关系,SSO认证中心对用户身份正确性的判断会通过某种方法告之Web应用,而且判断结果必须被Web应用信任。

       CAS的结构是由两部分组成,服务端和客服端。

       1. CAS Server负责完成对用户的认证工作(即认证用户名/密码等凭证),可以自定义认证方式,例如:XML文件、JDBC、LDAP等,需要独立部署。

       2. CAS Client 负责部署在客户端(Web应用),原则上,CAS Client的部署意味着,当有对本地 Web 应用的受保护资源的访问请求,并且需要对请求方进行身份认证,Web应用不再接受任何的用户名密码等类似的Credentials(凭证),而是重定向到CAS Server进行认证。

       CAS术语介绍(此处不介绍代理认证模式的术语):

       CAS的核心就是其Ticket,及其在Ticket之上的一系列处理操作

       1. TGT(Ticket Grangting Ticket)
          TGT是CAS为用户签发的登录票据,拥有了TGT,用户就可以证明自己在CAS成功登录过。TGT封装了Cookie值以及此Cookie值对应的用户信息。用户在CAS认证成功后,CAS生成cookie(叫TGC),写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值。当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值为key查询缓存中有无TGT ,如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。

       2. TGC (Ticket-granting cookie)
           存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,建议使用安全通道传输(Https),是CAS Server用来明确用户身份的凭证。TGC有自己的存活周期,通过grantingTimeout来设置CAS TGC存活周期,参数默认是120 分钟,在合适的范围内设置最小值,太短,会影响 SSO 体验,太长,会增加安全性风险。

       3. ST(Service Ticket)
           ST是CAS为用户签发的访问某一service的票据。用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。ST只能使用一次、在一段时间内失效、是基于随机数生成的。

       CAS登录主要流程,如下图:

cas_protocol-1.jpg

        登录具体流程:

         1. 用户第一次访问一个某应用(CAS Client)受保护资源时,例如:http://192.168.1.90:8081/web1/index,web应用的CAS  AuthenticationFilter会拦截此请求,生成service参数,redirect到CAS服务端的login接口,url为http://cas.itcast.cn/login?service=http%3A%2F%2Flocalhost%3A8180%2Fcas_app1;

         2. 在CAS服务端的登录页面输入用户名和密码认证成功后,CAS服务器会生成认证cookie(即TGC),写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值;

         3. CAS 服务器根据service参数生成ticket(即ST),ticket会保存到服务器,也会拼在url后面,然后将请求redirect回客户web应用,url 为http://192.168.1.90:8081/web1/index?ticket=ST-5-Sx6eyvj7cPPCfn0pMZuMwnbMvxpCBcNAIi6-20 ;

         4. web应用的AuthenticationFilter拦截后发现存在ticket参数则跳过,由其后面的TicketValidationFilter 处理,TicketValidationFilter 会利用httpclient工具访问cas服务端的serviceValidate接口,将ticket 、service 都传到此接口,由此接口验证ticket的有效性,TicketValidationFilter如果验证成功,则把用户信息写入web 应用的session里;

        5. 如果在此浏览器里访问其他web应用时,AuthenticationFilter在session里读取不到用户信息,会去CAS的login接口认证,但这时CAS服务端会读取到浏览器传来的cookie(TGC),所以CAS服务端不会要求用户去登录页面登录,只是会根据service参数生成一个ticket ,然后再和web应用做一个验证ticket 的交互(即步骤3和步骤4)。

         CAS登录主要流程,如下图:

\

         登出具体流程:

        1. web应用把登出(logout)请求redirect到CAS服务端,http://cas.itcast.cn/logout;

        2. CAS服务端接到logout请求后,会检测用户的TCG Cookie,把对应的session清除;

       3. CAS服务端同时会找到所有通过该TGC SSO登录的web应用URL,发送http请求,所有的回调请求中,都包含一个参数logoutRequest;

      4. 所有web应用(CAS client)的SingleSignOutFilter过滤器拦截服务端发来的http请求,会解析logoutRequest参数,取得sessionId,根据这个Id取得session后,把session清除。


  在配置单点登出的时候,除了配置SingleSignOutFilter外,一定要配置SingleSignOutHttpSessionListenerlistener监听器,否则web应用很容易导致内存溢出。

        web应用的CAS登出配置,特别注意,如果有配置CAS client Filter,则CAS Single Sign Out Filter必须要放到CAS client Filter之前。

<filter>
   <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>/*</url-pattern>
</filter-mapping>
<listener>
    <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>

         SingleSignOutFilter的整体逻辑,如一下代码:

        如果参数存在ticket参数,则把session放到sessionMappingStorage,如果参数中存在logoutRequest,则注销session,那什么时候去注销sessionMappingStorage的东西呢?这是靠SingleSignOutHttpSessionListener来实现的,当有session被销毁的时候,触发将sessionMappingStorage中对应sessionid中的数据删除。

  public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        // 转换参数
    	final HttpServletRequest request = (HttpServletRequest) servletRequest;
    	//判断参数中是否具有artifactParameterName属性指定的参数名称,默认是ticket
        if (handler.isTokenRequest(request)) {
        	// 如果存在,在本地sessionMappingStorage中记录session。
            handler.recordSession(request); 
        } else if (handler.isLogoutRequest(request)) {//判断是否具有logoutParameterName参数指定的参数,默认参数名称为logoutRequest
        	// 如果存在,则在sessionMappingStorage中删除记录,并注销session。
        	handler.destroySession(request);
            // 注销session后,立刻停止执行后面的过滤器
            return;
        } else {
            log.trace("Ignoring URI " + request.getRequestURI());
        }
        //条件都不满足,继续执行下面的过滤器
        filterChain.doFilter(servletRequest, servletResponse);
    }

       SingleSignOutHttpSessionListener实现了javax.servlet.http.HttpSessionListener接口,用于监听session销毁事件,用于在Cas客户端应用中的Session过期时将其从对应的映射关系中移除。

public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
		  
	private Log log = LogFactory.getLog(getClass());
	  
	private SessionMappingStorage SESSION_MAPPING_STORAGE;
	      
	public void sessionCreated(final HttpSessionEvent event) {
	   	// nothing to do at the moment
	}
	 
	//session销毁时
	public void sessionDestroyed(final HttpSessionEvent event) {
	    if (SESSION_MAPPING_STORAGE == null) {//如果为空,创建一个sessionMappingStorage 对象
	        SESSION_MAPPING_STORAGE = getSessionMappingStorage();
	    }
	    final HttpSession session = event.getSession();//取得当然要销毁的session对象
	     
	    if (log.isDebugEnabled()) {
	         log.debug("Removing HttpSession: " + session.getId());
	    }
	    //从SESSION_MAPPING_STORAGE map根据sessionId移去session对象
	    SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
 }
	 
	/**
	  * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same
	  * instance of the object.  It assumes this because it generally lazily calls the method.
	  * 
	  * @return the SessionMappingStorage
	  */
     protected static SessionMappingStorage getSessionMappingStorage() {
     	return SingleSignOutFilter.getSessionMappingStorage();
     }
}

       CAS还有一种高级用法,代理模式,场景:用户访问helloservice,helloservice又依赖于helloservice2 来获取一些信息,假 设helloservice2也是需要对User进行身份验证才能访问,那么,为了不影响用户体验(过多的重定向导致User的IE 窗口不停闪动),CAS引入了一种Proxy认证机制,即CAS Client可以代理用户去访问其它Web应用。由于这种用法本人没有搞懂,所以不在这里多说了。

        CAS的优点:
      1. 开源的、多协议的SSO 解决方案; Protocols : Custom Protocol 、 CAS 、 OAuth 、 OpenID 、 RESTful API 、 SAML1.1 、 SAML2.0 等。
        2. 支持多种认证机制: Active Directory 、 JAAS 、 JDBC 、 LDAP 、 X.509 Certificates 等,可自定义;
        3. 安全策略:使用票据( Ticket )来实现支持的认证协议;
        4. 支持授权:可以决定哪些服务可以请求和验证服务票据( Service Ticket );
       5. 提供高可用性:通过把认证过的状态数据存储在 TicketRegistry 组件中,这些组件有很多支持分布式环境的实现, 如: BerkleyDB 、Default 、EhcacheTicketRegistry 、JDBCTicketRegistry 、JBOSS TreeCache 、JpaTicketRegistry 、 MemcacheTicketRegistry 等;
       6. 支持多种客户端: Java 、 .Net 、 PHP 、 Perl 、 Apache, uPortal 等。

       7. 没有跨域问题, TGC-Cookie 只是跟 CAS Server 相关, CAS Server就只有一个。

       CAS的缺点:
       1. 框架比较重量级;
       2. 配置比较多。

PS:本文有些是自己写的,有些是网上抄的,如有发现写错之处请指正,共同学习,共同进步,谢谢。

猜你喜欢

转载自blog.csdn.net/romantic_PK/article/details/81483977