CAS研究(四)-登出/logout

很多童鞋对单点登出不是很理解,下面我们来看看单点登出到底做了什么东西,

我们来看看怎么从配置到代码的。

1)web.xml

com.bingo.tfp.web.init.SafeDispatcherServlet

<servlet-mapping>
<servlet-name>cas</servlet-name>
<url-pattern>/logout</url-pattern>
</servlet-mapping>

从上面可以知道,所有/logout的请求都交给SafeDispatcherServlet去分发了,查看代码可以知道这个Servlet只是对 org.springframework.web.servlet.DispatcherServlet一次包装,将所有请求都交给 org.springframework.web.servlet.DispatcherServlet去处理了。

2)cas-servlet.xml

handlerMappingC的bean里面有一段配置:

<prop key="/logout">
logoutController
</prop>

也就是说,所有/logout的请求,都交给一个beanid为logoutController的Bean去处理了,那么我们看看com.bingo.tfp.web.LogoutController到底做了什么事情,

<bean id="logoutController" class="com.bingo.tfp.web.LogoutController"
p:centralAuthenticationService-ref="centralAuthenticationService"
p:logoutView="casLogoutView"
p:warnCookieGenerator-ref="warnCookieGenerator"
p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"
p:followServiceRedirects="true"/>
我们看看源码是怎么操作的:


  1. protected ModelAndView handleRequestInternal( 
  2.      final HttpServletRequest request, final HttpServletResponse response) 
  3.      throws Exception { 
  4.     //取得TGT_ID 
  5.      final String ticketGrantingTicketId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request); 
  6.      // 取得service参数数据,这个参数是可选参数 
  7.      final String service = request.getParameter("service"); 
  8.       
  9.      //如果TGT不为空 
  10.      if (ticketGrantingTicketId != null) { 
  11.         //那么在centralAuthenticationService中销毁 
  12.          this.centralAuthenticationService 
  13.              .destroyTicketGrantingTicket(ticketGrantingTicketId); 
  14.          //ticketGrantingTicketCookieGenerator 中销毁cookie 
  15.          this.ticketGrantingTicketCookieGenerator.removeCookie(response); 
  16.          //warnCookieGenerator 中销毁 
  17.          this.warnCookieGenerator.removeCookie(response); 
  18.      } 
  19.      // 如果参数:followServiceRedirects为true 同时service不会空的时候,跳转到service指定的URL 
  20.      if (this.followServiceRedirects && service != null) { 
  21.          return new ModelAndView(new RedirectView(service)); 
  22.      } 
  23.      //否则,跳转到logoutView指定的页面 
  24.      return new ModelAndView(this.logoutView); 
   protected ModelAndView handleRequestInternal(
        final HttpServletRequest request, final HttpServletResponse response)
        throws Exception {
    	//取得TGT_ID
        final String ticketGrantingTicketId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);
        // 取得service参数数据,这个参数是可选参数
        final String service = request.getParameter("service");
        
        //如果TGT不为空
        if (ticketGrantingTicketId != null) {
        	//那么在centralAuthenticationService中销毁
            this.centralAuthenticationService
                .destroyTicketGrantingTicket(ticketGrantingTicketId);
            //ticketGrantingTicketCookieGenerator 中销毁cookie
            this.ticketGrantingTicketCookieGenerator.removeCookie(response);
            //warnCookieGenerator 中销毁
            this.warnCookieGenerator.removeCookie(response);
        }
        // 如果参数:followServiceRedirects为true 同时service不会空的时候,跳转到service指定的URL
        if (this.followServiceRedirects && service != null) {
            return new ModelAndView(new RedirectView(service));
        }
        //否则,跳转到logoutView指定的页面
        return new ModelAndView(this.logoutView);
    }

是不是很简单,那么有童鞋要问了,那么时候访问客户端呢?不要着急,我们在来看看

  1. this.centralAuthenticationService 
  2.                .destroyTicketGrantingTicket(ticketGrantingTicketId); 
 this.centralAuthenticationService
                .destroyTicketGrantingTicket(ticketGrantingTicketId);

做了什么事情:

  1. public void destroyTicketGrantingTicket(final String ticketGrantingTicketId) { 
  2.         //断言参数不能空 
  3.         Assert.notNull(ticketGrantingTicketId); 
  4.          
  5.         if (log.isDebugEnabled()) { 
  6.             log.debug("Removing ticket [" + ticketGrantingTicketId + "] from registry."); 
  7.         } 
  8.         // 从票据仓库中取得TGT票据 
  9.         final TicketGrantingTicket ticket = (TicketGrantingTicket) this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class); 
  10.         //如果票据为空,则直接返回 
  11.         if (ticket == null) { 
  12.             return
  13.         } 
  14.  
  15.         if (log.isDebugEnabled()) { 
  16.             log.debug("Ticket found.  Expiring and then deleting."); 
  17.         } 
  18.         //叫票据注销,也就是设置为期满(或者叫做过期) 
  19.         ticket.expire(); 
  20.         //在票据仓库中删除该票据 
  21.         this.ticketRegistry.deleteTicket(ticketGrantingTicketId); 
  22.     } 
public void destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
        //断言参数不能空
    	Assert.notNull(ticketGrantingTicketId);
    	
        if (log.isDebugEnabled()) {
            log.debug("Removing ticket [" + ticketGrantingTicketId + "] from registry.");
        }
        // 从票据仓库中取得TGT票据
        final TicketGrantingTicket ticket = (TicketGrantingTicket) this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
        //如果票据为空,则直接返回
        if (ticket == null) {
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Ticket found.  Expiring and then deleting.");
        }
        //叫票据注销,也就是设置为期满(或者叫做过期)
        ticket.expire();
        //在票据仓库中删除该票据
        this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
    }
很奇怪是不是,居然还没有请求客户端的东西,别急嘛。我们再来看看
  1. ticket.expire(); 
ticket.expire();
做了什么,这个是TicketGrantingTicketImpl来实现的。

public synchronized void expire() {
        this.expired = true;
        logOutOfServices();
    }
是不是发现新大陆了?

下面是logOutOfServices()方法的源代码:

  1. private void logOutOfServices() { 
  2.        for (final Entry<String, Service> entry : this.services.entrySet()) { 
  3.  
  4.            if (!entry.getValue().logOutOfService(entry.getKey())) { 
  5.                LOG.warn("Logout message not sent to [" + entry.getValue().getId() + "]; Continuing processing...");    
  6.            } 
  7.        } 
  8.    } 
 private void logOutOfServices() {
        for (final Entry<String, Service> entry : this.services.entrySet()) {

            if (!entry.getValue().logOutOfService(entry.getKey())) {
                LOG.warn("Logout message not sent to [" + entry.getValue().getId() + "]; Continuing processing...");   
            }
        }
    }
哇,原来在TGT票据里面有个Entry来保存用户访问过的service对象,key是对应service的seesionID,那么是不是使用https请求客户无端呢?看看AbstractWebApplicationService类就知道咯,

  1. public synchronized boolean logOutOfService(final String sessionIdentifier) { 
  2.        if (this.loggedOutAlready) { 
  3.            return true
  4.        } 
  5.  
  6.        LOG.debug("Sending logout request for: " + getId()); 
  7.  
  8.        final String logoutRequest = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"" 
  9.            + GENERATOR.getNewTicketId("LR"
  10.            + "\" Version=\"2.0\" IssueInstant=\"" + SamlUtils.getCurrentDateAndTime() 
  11.            + "\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@</saml:NameID><samlp:SessionIndex>" 
  12.            + sessionIdentifier + "</samlp:SessionIndex></samlp:LogoutRequest>"
  13.         
  14.        this.loggedOutAlready = true
  15.         
  16.        if (this.httpClient != null) { 
  17.            return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest, true); 
  18.        } 
  19.         
  20.        return false
  21.    } 
 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, true);
        }
        
        return false;
    }
怎么样,这样是不是对单点登出细节比较清楚呢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值