1. 程式人生 > >cas+shiro統一登出原理解析

cas+shiro統一登出原理解析

轉載:https://www.cnblogs.com/chenrd/p/5164706.html

1,客戶端傳送一個登出請求到cas server,跟蹤casorg.jasig.cas.CentralAuthenticationServiceImpl類的destroyTicketGrantingTicket登出方法,

服務端登出程式碼

@Audit(
            action="TICKET_GRANTING_TICKET_DESTROYED",
            actionResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOLVER",
            resourceResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
    @Profiled(tag = "DESTROY_TICKET_GRANTING_TICKET", logFailuresSeparately = false)
    @Transactional(readOnly = false)
    @Override
    public List<LogoutRequest> destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
        Assert.notNull(ticketGrantingTicketId);

        logger.debug("Removing ticket [{}] from registry.", ticketGrantingTicketId);
        final TicketGrantingTicket ticket = this.ticketRegistry.getTicket(ticketGrantingTicketId,
                TicketGrantingTicket.class);

        if (ticket == null) {
            logger.debug("TicketGrantingTicket [{}] cannot be found in the ticket registry.", ticketGrantingTicketId);
            return Collections.emptyList();
        }

        logger.debug("Ticket found. Processing logout requests and then deleting the ticket...");
    //在這裡cas server會去根據客戶端帶過來的ticket找到所有在cas server服務註冊過的cas client server,讓後對這些客戶端服務傳送一個http請求,客戶端接受請求,刪除//本身的ticket及登出session,cas server傳送請求看下一段程式碼
        final List<LogoutRequest> logoutRequests = logoutManager.performLogout(ticket);
        this.ticketRegistry.deleteTicket(ticketGrantingTicketId);

        return logoutRequests;
    }
public List<LogoutRequest> performLogout(final TicketGrantingTicket ticket) {
        final Map<String, Service> services;
        // synchronize the retrieval of the services and their cleaning for the TGT
        // to avoid concurrent logout mess ups
        synchronized (ticket) {
            services = ticket.getServices();
            ticket.removeAllServices();
        }
        ticket.markTicketExpired();

        final List<LogoutRequest> logoutRequests = new ArrayList<LogoutRequest>();
        // if SLO is not disabled
        if (!disableSingleSignOut) {
            // through all services

   //迴圈遍歷客戶端的server
            for (final String ticketId : services.keySet()) {
                final Service service = services.get(ticketId);
                // it's a SingleLogoutService, else ignore
                if (service instanceof SingleLogoutService) {
                    final SingleLogoutService singleLogoutService = (SingleLogoutService) service;
                    // the logout has not performed already
                    if (!singleLogoutService.isLoggedOutAlready()) {
                        final LogoutRequest logoutRequest = new LogoutRequest(ticketId, singleLogoutService);
                        // always add the logout request
                        logoutRequests.add(logoutRequest);
                        final RegisteredService registeredService = servicesManager.findServiceBy(service);
                        // the service is no more defined, or the logout type is not defined or is back channel
                        if (registeredService == null || registeredService.getLogoutType() == null
                                || registeredService.getLogoutType() == LogoutType.BACK_CHANNEL) {
                            // perform back channel logout
                
                //向客戶端傳送一個請求,
                            if (performBackChannelLogout(logoutRequest)) {
                                logoutRequest.setStatus(LogoutRequestStatus.SUCCESS);
                            } else {
                                logoutRequest.setStatus(LogoutRequestStatus.FAILURE);
                                LOGGER.warn("Logout message not sent to [{}]; Continuing processing...",
                                        singleLogoutService.getId());
                            }
                        }
                    }
                }
            }
        }

        return logoutRequests;
    }

下面在來看一下客戶端接收的程式碼,客戶段單點登出必須配置SingleSignOutFilter,前文cas server傳送一個登出請求回來的時候會被接收處理

public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        //重點的兩個IF判斷,同時也是邏輯處理,第一是判斷是不是第一次登入的時候會進入方法往cas SessionMappingStorage的新增ticket,sessionId鍵值對
        if (handler.isTokenRequest(request)) {
            LOG.warn("第{}次進來對映,sessionId={}", index++, request.getSession().getId());
            handler.recordSession(request);
      //判斷是否是登出請求,如果是登出請求進入邏輯,登出session同時刪除SessionMappingStorage的鍵值對(就是這裡實現了統一登出)
        } else if (handler.isLogoutRequest(request)) {
            LOG.warn("第{}進來刪除對映,sessionId={}", removeIndex++, request.getSession().getId());
            handler.destroySession(request);
            // Do not continue up filter chain
            return;
        } else {
            log.trace("Ignoring URI " + request.getRequestURI());
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }

/jaxws/services/**=anon我的shiro配置了web service不進行身份驗證也是無效的,因為SingleSignOutFilter過濾器的優先順序在shiro過濾前面,所以才會傳送這個問題。