CAS 服務端登錄驗證流程(三)

接着上一次結束的地方 WEB-INF/login-webflow.xml

<view-state id="viewLoginForm" view="casLoginView" model="credentials">  
    <binder>  
        <binding property="username" />  
        <binding property="password" />  
    </binder>  
    <on-entry>  
        <set name="viewScope.commandName" value="'credentials'" />  
    </on-entry>  
    <!--流程跑到這裏開始,執行 AuthenticationViaFormAction 的 doBind 方法-->
    <!--其配置在 WEB-INF/cas-servlet.xml-->
    <transition on="submit" bind="true" validate="true" to="realSubmit">  
        <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />  
    </transition>  
</view-state>  

查看 WEB-INF/cas-servlet.xml

<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction"
    p:centralAuthenticationService-ref="centralAuthenticationService"
    p:warnCookieGenerator-ref="warnCookieGenerator"/>

進入源碼,AuthenticationViaFormAction 的 doBind 方法

public final void doBind(final RequestContext context, final Credentials credentials) throws Exception {  
    final HttpServletRequest request = WebUtils.getHttpServletRequest(context);  
    <!--將 Request 裏的賬號密碼信息存入 credentials,封裝成一個用戶憑證-->
    if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) {  
        this.credentialsBinder.bind(request, credentials);  
    }  
}  

登錄流程流轉到第一個state(realSubmit)

<action-state id="realSubmit">  
    <!--配置在 WEB-INF/cas-servlet.xml-->
    <evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" />  
    <transition on="warn" to="warn" /><!-- 警告,轉向其他站點前提示我 -->  
    <transition on="success" to="sendTicketGrantingTicket" /><!-- 成功 -->  
    <transition on="error" to="generateLoginTicket" /><!-- 錯誤 -->  
    <transition on="accountDisabled" to="casAccountDisabledView" />  
    <transition on="mustChangePassword" to="casMustChangePassView" />  
    <transition on="accountLocked" to="casAccountLockedView" />  
    <transition on="badHours" to="casBadHoursView" />  
    <transition on="badWorkstation" to="casBadWorkstationView" />  
    <transition on="passwordExpired" to="casExpiredPassView" />  
</action-state> 

進入源碼,AuthenticationViaFormAction 的 submit 方法

public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception {  
    // Validate login ticket 
    <!--從 FlowScope(類似於 Session)取出 LT-->
    final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context);  
    <!--從 Request 參數中取出 LT-->
    final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context);  
    <!--判斷兩邊的 LT 是否相同  -->
    if (!authoritativeLoginTicket.equals(providedLoginTicket)) {  
        this.logger.warn("Invalid login ticket " + providedLoginTicket);  
        final String code = "INVALID_TICKET";  
        messageContext.addMessage(new MessageBuilder().error().code(code).arg(providedLoginTicket).defaultText(code).build());  
        return "error";  
    }  
    <!--從 requestScope 和 FlowScope 中獲取 TGT  -->
    final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);  
    <!--從 FlowScope 中獲取 service  -->
    final Service service = WebUtils.getService(context);  
    <!--如果存在 renew、TGT 和 service,可直接構造 ST,早一步到達 “warn”-->
    if (StringUtils.hasText(context.getRequestParameters().get("renew"))   
            && ticketGrantingTicketId != null && service != null) {  

        try {  
            final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(  
                ticketGrantingTicketId, service, credentials);  
            WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);  
            putWarnCookieIfRequestParameterPresent(context);  
            return "warn";  
        } catch (final TicketException e) {  
            if (isCauseAuthenticationException(e)) {  
                populateErrorsInstance(e, messageContext);  
                return getAuthenticationExceptionEventId(e);  
            }  

            this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);  
            if (logger.isDebugEnabled()) {  
                logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);  
            }  
        }  
    }  

    try {  
        <!--1.驗證用戶賬號 2.構造 TGT,同時把 TGT 緩存到 cache<ticketId, TGT> 3.TGT 存入 requestScope-->
        WebUtils.putTicketGrantingTicketInRequestScope(context,   
            this.centralAuthenticationService.createTicketGrantingTicket(credentials));  
        <!--如果 Request 參數中提供 warn,存入 Cookie-->
        putWarnCookieIfRequestParameterPresent(context);  
        return "success";  
    } catch (final TicketException e) {  
        populateErrorsInstance(e, messageContext);  
        if (isCauseAuthenticationException(e))  
            return getAuthenticationExceptionEventId(e);  
        return "error";  
    }  
}  

登錄流程流轉到第二個state(sendTicketGrantingTicket)

<action-state id="sendTicketGrantingTicket">  
    <!--配置在 WEB-INF/cas-servlet.xml-->
    <evaluate expression="sendTicketGrantingTicketAction" />  
    <transition to="serviceCheck" />  
</action-state>  

進入源碼,SendTicketGrantingTicketAction 的 doExecute 方法

protected Event doExecute(final RequestContext context) {  
    <!--從 requestScope 和 FlowScope 中獲取 TGT  -->
    final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);   
    final String ticketGrantingTicketValueFromCookie = (String) context.getFlowScope().get("ticketGrantingTicketId");  

    if (ticketGrantingTicketId == null) {  
        return success();  
    }  
    <!--Cookie 存儲 TGC-->  
    this.ticketGrantingTicketCookieGenerator.addCookie(WebUtils.getHttpServletRequest(context), 
        WebUtils.getHttpServletResponse(context), ticketGrantingTicketId);  

    if (ticketGrantingTicketValueFromCookie != null && !ticketGrantingTicketId.equals(ticketGrantingTicketValueFromCookie)) {  
        this.centralAuthenticationService  
            .destroyTicketGrantingTicket(ticketGrantingTicketValueFromCookie);  
    }  

    return success();  
}  

流程流轉到第三個state(serviceCheck)

<decision-state id="serviceCheck">  
    <if test="flowScope.service != null" then="generateServiceTicket" else="viewGenericLoginSuccess" />  
</decision-state>  

由於 FlowScope 中存在 service,流程流轉到第四個state(generateServiceTicket)

<action-state id="generateServiceTicket">  
    <!--配置在 WEB-INF/cas-servlet.xml-->
    <evaluate expression="generateServiceTicketAction" />  
    <transition on="success" to ="warn" />  
    <transition on="error" to="generateLoginTicket" />  
    <transition on="gateway" to="gatewayServicesManagementCheck" />  
</action-state> 

進入源碼,GenerateServiceTicketAction 的 doExecute 方法

protected Event doExecute(final RequestContext context) {  
    <!--獲取service  -->
    final Service service = WebUtils.getService(context);  
    <!--獲取TGT  -->
    final String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context);  

    try {  
        <!--生成 ST(ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org)  -->
        final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicket, service);  
        <!--ST 存入 requestScope-->
        WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);  
        return success();  
    } catch (final TicketException e) {  
        if (isGatewayPresent(context)) {  
            return result("gateway");  
        }  
    }  

    return error();  
}  

流程流轉到第五個state(warn)

<decision-state id="warn">  
    <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" />  
</decision-state>

由於 FlowScope 中不存在 warnCookieValue(這個值在默認登錄表單中可以打鉤選中,表示在轉向其他網站前提供一個提示),流程流轉到第六個state(redirect)

<action-state id="redirect">  
    <!--從 requestScope 中獲取 serviceTicket,構造 response 對象,並放入 requestScope-->
    <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)"   
        result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" />  
    <transition to="postRedirectDecision" />  
</action-state> 

流程流轉到第七個state(postRedirectDecision)

<decision-state id="postRedirectDecision">  
    <if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView" />  
</decision-state>  

由於 requestScope.response 是 GET 類型,登錄流程流轉到第八個state(redirectView)

<end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" />  

重定向 URL:service,即最初訪問的集成 CAS 單點登錄的應用系統(http://pomer.com:8080/client?ticket=ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章