成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

CAS 5.2.x 單點登錄 - 實現(xiàn)原理及源碼淺析

elisa.yang / 1627人閱讀

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

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

一、Session 和 Cookie

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

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

二、CAS 流程圖

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

瀏覽器與 APP01 服務(wù)端

瀏覽器第一次訪問受保護的 APP01 服務(wù)端,由于未經(jīng)授權(quán)而被攔截并重定向到 CAS 服務(wù)端。

瀏覽器第一次與 CAS 服務(wù)端通訊,鑒權(quán)成功后由 CAS 服務(wù)端創(chuàng)建全局會話 SSO Session,生成全局會話標識 TGT 并存儲在瀏覽器 Cookie 中。

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

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

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

瀏覽器與 APP01 建立會話。

瀏覽器與 APP02 服務(wù)端

瀏覽器第一次訪問受保護的 APP02 服務(wù)端,由于未經(jīng)授權(quán)而被攔截并重定向到 CAS 服務(wù)端。

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

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

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

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

瀏覽器與 APP02 建立會話。

三、相關(guān)源碼 3.1 CAS客戶端 3.1.1 根據(jù)是否已登錄進行攔截跳轉(zhuǎn)

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

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

// Session已經(jīng)登錄
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 文件內(nèi)容示例:


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

最后將 XML 字符串轉(zhuǎn)換為對象 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服務(wù)端 3.2.1 用戶密碼校驗

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


    
    
    
    
    
    

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

定位到該流程對應(yīng)的實現(xiàn)類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

經(jīng)過層層過濾,得到執(zhí)行校驗的AcceptUsersAuthenticationHandler和待校驗的UsernamePasswordCredential。

執(zhí)行校驗,進入
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();
    // 根據(jù)用戶名取得緩存中的密碼
    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 {
        // 根據(jù)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 存儲在服務(wù)端的一個 Map 集合中:
org.apereo.cas.AbstractCentralAuthenticationService#getTicket(java.lang.String, java.lang.Class)

3.2.3 客戶端Ticket校驗

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

從 Ticket 倉庫中,根據(jù) 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());
    }
}

根據(jù) Ticket 獲取已登錄用戶:

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

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

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71024.html

相關(guān)文章

  • cas工作原理淺析與總結(jié)

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

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

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

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

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

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

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

    妤鋒シ 評論0 收藏0

發(fā)表評論

0條評論

elisa.yang

|高級講師

TA的文章

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