国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

CAS 5.2.x 單點登錄 - 實現原理及源碼淺析

elisa.yang / 1483人閱讀

摘要:上一篇文章簡單介紹了在本地開發環境中搭建服務端和客戶端,對單點登錄過程有了一個直觀的認識之后,本篇將探討單點登錄的實現原理。因此引入服務端作為用戶信息鑒別和傳遞中介,達到單點登錄的效果。為該流程的實現類。表示對返回結果的處理。

上一篇文章簡單介紹了 CAS 5.2.2 在本地開發環境中搭建服務端和客戶端,對單點登錄過程有了一個直觀的認識之后,本篇將探討 CAS 單點登錄的實現原理。

一、Session 和 Cookie

HTTP 是無狀態協議,客戶端與服務端之間的每一次通訊都是獨立的,而會話機制可以讓服務端鑒別每次通訊過程中的客戶端是否是同一個,從而保證業務的關聯性。Session 是服務器使用一種類似于散列表的結構,用來保存用戶會話所需要的信息。Cookie 作為瀏覽器緩存,存儲 Session ID 以到達會話跟蹤的目的。

由于 Cookie 的跨域策略限制,Cookie 攜帶的會話標識無法在域名不同的服務端之間共享。
因此引入 CAS 服務端作為用戶信息鑒別和傳遞中介,達到單點登錄的效果。

二、CAS 流程圖

官方流程圖,地址:https://apereo.github.io/cas/...

瀏覽器與 APP01 服務端

瀏覽器第一次訪問受保護的 APP01 服務端,由于未經授權而被攔截并重定向到 CAS 服務端。

瀏覽器第一次與 CAS 服務端通訊,鑒權成功后由 CAS 服務端創建全局會話 SSO Session,生成全局會話標識 TGT 并存儲在瀏覽器 Cookie 中。

瀏覽器重定向到 APP01,重寫 URL 地址帶上全局會話標識 TGT。

APP01 拿到全局會話標識 TGT 后向 CAS 服務端請求校驗,若校驗成功,則 APP01 會獲取到已經登錄的用戶信息。

APP01 創建局部會話 Session,并將 SessionID 存儲到瀏覽器 Cookie 中。

瀏覽器與 APP01 建立會話。

瀏覽器與 APP02 服務端

瀏覽器第一次訪問受保護的 APP02 服務端,由于未經授權而被攔截并重定向到 CAS 服務端。

瀏覽器第二次與 CAS 服務端通訊,CAS 校驗 Cookie 中的全局會話標識 TGT。

瀏覽器重定向到 APP02,重寫 URL 地址帶上全局會話標識 TGT。

APP02 拿到全局會話標識 TGT 后向 CAS 服務端請求校驗,若校驗成功,則 APP02 會獲取到已經登錄的用戶信息。

APP02 創建局部會話 Session,并將 SessionID 存儲到瀏覽器 Cookie 中。

瀏覽器與 APP02 建立會話。

三、相關源碼 3.1 CAS客戶端 3.1.1 根據是否已登錄進行攔截跳轉

以客戶端攔截器作為入口,對于用戶請求,如果是已經校驗通過的,直接放行:
org.jasig.cas.client.authentication.AuthenticationFilter#doFilter

// 不進行攔截的請求地址
if (isRequestUrlExcluded(request)) {
    logger.debug("Request is ignored.");
    filterChain.doFilter(request, response);
    return;
}

// Session已經登錄
final HttpSession session = request.getSession(false);
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
if (assertion != null) {
    filterChain.doFilter(request, response);
    return;
}

// 從請求中獲取ticket
final String serviceUrl = constructServiceUrl(request, response);
final String ticket = retrieveTicketFromRequest(request);
final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
    filterChain.doFilter(request, response);
    return;
}

否則進行重定向:
org.jasig.cas.client.authentication.AuthenticationFilter#doFilter

this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);

對于Ajax請求和非Ajax請求的重定向,進行分別處理:
org.jasig.cas.client.authentication.FacesCompatibleAuthenticationRedirectStrategy#redirect

public void redirect(final HttpServletRequest request, final HttpServletResponse response,
        final String potentialRedirectUrl) throws IOException {

    if (CommonUtils.isNotBlank(request.getParameter(FACES_PARTIAL_AJAX_PARAMETER))) {
        // this is an ajax request - redirect ajaxly
        response.setContentType("text/xml");
        response.setStatus(200);

        final PrintWriter writer = response.getWriter();
        writer.write("");
        writer.write(String.format("",
                potentialRedirectUrl));
    } else {
        response.sendRedirect(potentialRedirectUrl);
    }
}
3.1.2 校驗Ticket

如果請求中帶有 Ticket,則進行校驗,校驗成功返回用戶信息:
org.jasig.cas.client.validation.AbstractTicketValidationFilter#doFilter

final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
request.setAttribute(CONST_CAS_ASSERTION, assertion);

打斷點得知返回的信息為 XML 格式字符串:
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator#validate

logger.debug("Retrieving response from server.");
final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);

XML 文件內容示例:


    
        casuser
        
            UsernamePasswordCredential
            true
            2018-03-25T22:09:49.768+08:00[GMT+08:00]
            AcceptUsersAuthenticationHandler
            AcceptUsersAuthenticationHandler
            false
            
    

最后將 XML 字符串轉換為對象 org.jasig.cas.client.validation.Assertion,并存儲在 Session 或 Request 中。

3.1.3 重寫Request請求

定義過濾器:
org.jasig.cas.client.util.HttpServletRequestWrapperFilter#doFilter

其中定義 CasHttpServletRequestWrapper,重寫 HttpServletRequestWrapperFilter:

final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {

        private final AttributePrincipal principal;

        CasHttpServletRequestWrapper(final HttpServletRequest request, final AttributePrincipal principal) {
            super(request);
            this.principal = principal;
        }

        public Principal getUserPrincipal() {
            return this.principal;
        }

        public String getRemoteUser() {
            return principal != null ? this.principal.getName() : null;
        }
        // 省略其他代碼

這樣使用以下代碼即可獲取已登錄用戶信息。

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
3.2 CAS服務端 3.2.1 用戶密碼校驗

服務端采用了 Spirng Web Flow,以 login-webflow.xml 為入口:


    
    
    
    
    
    

action-state代表一個流程,其中 id 為該流程的標識。
evaluate expression為該流程的實現類。
transition表示對返回結果的處理。

定位到該流程對應的實現類authenticationViaFormAction,可知在項目啟動時實例化了對象AbstractAuthenticationAction

@ConditionalOnMissingBean(name = "authenticationViaFormAction")
@Bean
@RefreshScope
public Action authenticationViaFormAction() {
    return new InitialAuthenticationAction(initialAuthenticationAttemptWebflowEventResolver,
            serviceTicketRequestWebflowEventResolver,
            adaptiveAuthenticationPolicy);
}

在頁面上點擊登錄按鈕,進入:
org.apereo.cas.web.flow.actions.AbstractAuthenticationAction#doExecute
org.apereo.cas.authentication.PolicyBasedAuthenticationManager#authenticate

經過層層過濾,得到執行校驗的AcceptUsersAuthenticationHandler和待校驗的UsernamePasswordCredential

執行校驗,進入
org.apereo.cas.authentication.AcceptUsersAuthenticationHandler#authenticateUsernamePasswordInternal

@Override
protected HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential,
                                                             final String originalPassword) throws GeneralSecurityException {
    if (this.users == null || this.users.isEmpty()) {
        throw new FailedLoginException("No user can be accepted because none is defined");
    }
    // 頁面輸入的用戶名
    final String username = credential.getUsername();
    // 根據用戶名取得緩存中的密碼
    final String cachedPassword = this.users.get(username);

    if (cachedPassword == null) {
        LOGGER.debug("[{}] was not found in the map.", username);
        throw new AccountNotFoundException(username + " not found in backing map.");
    }
    // 校驗緩存中的密碼和用戶輸入的密碼是否一致
    if (!StringUtils.equals(credential.getPassword(), cachedPassword)) {
        throw new FailedLoginException();
    }
    final List list = new ArrayList<>();
    return createHandlerResult(credential, this.principalFactory.createPrincipal(username), list);
}
3.2.2 登錄頁Ticket校驗

在 login-webflow.xml 中定義了 Ticket 校驗流程:


    
    
    
    

org.apereo.cas.web.flow.TicketGrantingTicketCheckAction#doExecute

@Override
protected Event doExecute(final RequestContext requestContext) {
    // 從請求中獲取TicketID
    final String tgtId = WebUtils.getTicketGrantingTicketId(requestContext);
    if (!StringUtils.hasText(tgtId)) {
        return new Event(this, NOT_EXISTS);
    }

    String eventId = INVALID;
    try {
        // 根據TicketID獲取Tciket對象,校驗是否失效
        final Ticket ticket = this.centralAuthenticationService.getTicket(tgtId, Ticket.class);
        if (ticket != null && !ticket.isExpired()) {
            eventId = VALID;
        }
    } catch (final AbstractTicketException e) {
        LOGGER.trace("Could not retrieve ticket id [{}] from registry.", e.getMessage());
    }
    return new Event(this, eventId);
}

可知 Ticket 存儲在服務端的一個 Map 集合中:
org.apereo.cas.AbstractCentralAuthenticationService#getTicket(java.lang.String, java.lang.Class)

3.2.3 客戶端Ticket校驗

對于從 CAS 客戶端發送過來的 Ticket 校驗請求,則會進入服務端以下代碼:
org.apereo.cas.DefaultCentralAuthenticationService#validateServiceTicket

從 Ticket 倉庫中,根據 TicketID 獲取 Ticket 對象:

final ServiceTicket serviceTicket = this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class);

在同步塊中校驗 Ticket 是否失效,以及是否來自合法的客戶端:

synchronized (serviceTicket) {
    if (serviceTicket.isExpired()) {
        LOGGER.info("ServiceTicket [{}] has expired.", serviceTicketId);
        throw new InvalidTicketException(serviceTicketId);
    }

    if (!serviceTicket.isValidFor(service)) {
        LOGGER.error("Service ticket [{}] with service [{}] does not match supplied service [{}]",
                serviceTicketId, serviceTicket.getService().getId(), service);
        throw new UnrecognizableServiceForServiceTicketValidationException(serviceTicket.getService());
    }
}

根據 Ticket 獲取已登錄用戶:

final TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot();
final Authentication authentication = getAuthenticationSatisfiedByPolicy(root.getAuthentication(),
        new ServiceContext(selectedService, registeredService));
final Principal principal = authentication.getPrincipal();

最后將用戶信息返回給客戶端。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71024.html

相關文章

  • cas工作原理淺析與總結

    摘要:是大學發起的一個企業級的開源的項目,旨在為應用系統提供一種可靠的單點登錄解決方法屬于。實現原理是先通過的認證,然后向申請一個針對于的,之后在訪問時把申請到的針對于的以參數傳遞過去。后面的流程與上述流程步驟及以后步驟類似 CAS( Central Authentication Service )是 Yale 大學發起的一個企業級的、開源的項目,旨在為 Web 應用系統提供一種可靠的單點登...

    warkiz 評論0 收藏0
  • CAS 5.2.x 單點登錄 - 搭建服務端和客戶端

    摘要:一簡介單點登錄,簡稱為,是目前比較流行的企業業務整合的解決方案之一。客戶端攔截未認證的用戶請求,并重定向至服務端,由服務端對用戶身份進行統一認證。三搭建客戶端在官方文檔中提供了客戶端樣例,即。 一、簡介 單點登錄(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系...

    Lin_YT 評論0 收藏0
  • Python Flask單點登錄問題

    摘要:如果一旦加密算法泄露了,攻擊者可以在本地建立一個實現了登錄接口的假冒父應用,通過綁定來把子應用發起的請求指向本地的假冒父應用,并作出回應。 1.什么是單點登錄? 單點登錄(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。客戶端持有ID,服務端持有session...

    tuomao 評論0 收藏0
  • 號外:友戶通支持企業自有用戶中心啦

    摘要:針對這種情況,友戶通特定開發了聯邦用戶中心來支持企業的自有用戶中心。友戶通支持通過協議使用企業內部的支持協議的用戶中心賬號進行登錄。友戶通目前支持標準協議以及友戶通自定義協議可供企業集成。 友戶通做用友云的用戶系統也一年多了,經常聽實施、售前等說要私有化部署友戶通,原因無非是企業的考慮到用戶安全性和單一用戶賬號的需求。但由于用戶管理的復雜性,友戶通部署與維護并不容易,因此經常糾結在用戶...

    妤鋒シ 評論0 收藏0

發表評論

0條評論

elisa.yang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<