cas 解讀

前期準備:

2.應用系統webapp1(http://127.0.0.1:8090/webapp1/main.do)
3.應用系統webapp2(http://127.0.0.1:8091/webapp2/main.do)
4.CAS單點登陸服務器端(http://127.0.0.1:8081/cas-server/)

        本次討論包括CAS單點登陸服務器端的部分源碼,以及在此基礎上進行二次開發,所以須要修改部分CAS服務器端的源碼,源碼部分的修改在下面進行討論。關於CAS客戶端的源碼分析,請參考另外一篇文章http://blog.csdn.net/dovejing/article/details/44426547
其中cas-server-3.5.2-release.zip爲CAS服務器端的源碼zip包。html

web.xml部分代碼java

[html]  view plain  copy
 
  1. <servlet>  
  2.     <servlet-name>cas</servlet-name>  
  3.     <servlet-class>org.jasig.cas.web.init.SafeDispatcherServlet</servlet-class>  
  4.     <init-param>  
  5.         <param-name>publishContext</param-name>  
  6.         <param-value>false</param-value>  
  7.     </init-param>  
  8.     <load-on-startup>1</load-on-startup>  
  9. </servlet>  
  10.       
  11. <servlet-mapping>  
  12.     <servlet-name>cas</servlet-name>  
  13.     <url-pattern>/login</url-pattern>  
  14. </servlet-mapping>  
  15.       
  16. <servlet-mapping>  
  17.     <servlet-name>cas</servlet-name>  
  18.     <url-pattern>/logout</url-pattern>  
  19. </servlet-mapping>  
  20.   
  21. <servlet-mapping>  
  22.     <servlet-name>cas</servlet-name>  
  23.     <url-pattern>/validate</url-pattern>  
  24. </servlet-mapping>  
  25.   
  26. <servlet-mapping>  
  27.     <servlet-name>cas</servlet-name>  
  28.     <url-pattern>/serviceValidate</url-pattern>  
  29. </servlet-mapping>  
  30.   
  31. <servlet-mapping>  
  32.     <servlet-name>cas</servlet-name>  
  33.     <url-pattern>/samlValidate</url-pattern>  
  34. </servlet-mapping>  
  35.   
  36. <servlet-mapping>  
  37.     <servlet-name>cas</servlet-name>  
  38.     <url-pattern>/proxy</url-pattern>  
  39. </servlet-mapping>  
  40.   
  41. <servlet-mapping>  
  42.     <servlet-name>cas</servlet-name>  
  43.     <url-pattern>/proxyValidate</url-pattern>  
  44. </servlet-mapping>  
  45.   
  46. <servlet-mapping>  
  47.     <servlet-name>cas</servlet-name>  
  48.     <url-pattern>/CentralAuthenticationService</url-pattern>  
  49. </servlet-mapping>  
  50.   
  51. <servlet-mapping>  
  52.     <servlet-name>cas</servlet-name>  
  53.     <url-pattern>/services/add.html</url-pattern>  
  54. </servlet-mapping>  
  55.   
  56. <servlet-mapping>  
  57.     <servlet-name>cas</servlet-name>  
  58.     <url-pattern>/services/viewStatistics.html</url-pattern>  
  59. </servlet-mapping>  
  60.   
  61. <servlet-mapping>  
  62.     <servlet-name>cas</servlet-name>  
  63.     <url-pattern>/services/logout.html</url-pattern>  
  64. </servlet-mapping>  
  65.   
  66. <servlet-mapping>  
  67.     <servlet-name>cas</servlet-name>  
  68.     <url-pattern>/services/loggedOut.html</url-pattern>  
  69. </servlet-mapping>  
  70.   
  71. <servlet-mapping>  
  72.     <servlet-name>cas</servlet-name>  
  73.     <url-pattern>/services/manage.html</url-pattern>  
  74. </servlet-mapping>  
  75.   
  76. <servlet-mapping>  
  77.     <servlet-name>cas</servlet-name>  
  78.     <url-pattern>/services/edit.html</url-pattern>  
  79. </servlet-mapping>  
  80.   
  81. <servlet-mapping>  
  82.     <servlet-name>cas</servlet-name>  
  83.     <url-pattern>/openid/*</url-pattern>  
  84. </servlet-mapping>  
  85.   
  86. <servlet-mapping>  
  87.     <servlet-name>cas</servlet-name>  
  88.     <url-pattern>/services/deleteRegisteredService.html</url-pattern>  
  89. </servlet-mapping>  
  90.       
  91. <servlet-mapping>  
  92.     <servlet-name>cas</servlet-name>  
  93.     <url-pattern>/services/updateRegisteredServiceEvaluationOrder.html</url-pattern>  
  94. </servlet-mapping>  
  95.   
  96. <servlet-mapping>  
  97.     <servlet-name>cas</servlet-name>  
  98.     <url-pattern>/status</url-pattern>  
  99. </servlet-mapping>  
  100.   
  101. <servlet-mapping>  
  102.     <servlet-name>cas</servlet-name>  
  103.     <url-pattern>/authorizationFailure.html</url-pattern>  
  104. </servlet-mapping>  
  105.   
  106. <servlet-mapping>  
  107.     <servlet-name>cas</servlet-name>  
  108.     <url-pattern>/403.html</url-pattern>  
  109. </servlet-mapping>  
  110.       
  111. <servlet-mapping>  
  112.     <servlet-name>cas</servlet-name>  
  113.     <url-pattern>/error</url-pattern>  
  114. </servlet-mapping>  
  115.       
  116. <servlet-mapping>  
  117.     <servlet-name>cas</servlet-name>  
  118.     <url-pattern>/authcode</url-pattern>  
  119. </servlet-mapping>  

訪問集成了CAS單點登陸的應用系統webapp1

下面講一下CAS單點登陸服務器端的登陸流程,流程的配置在/WEB-INF/login-webflow.xml文件中。web

/WEB-INF/login-webflow.xml部分代碼spring

 

[html]  view plain  copy
 
  1. <var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />  

 

首先,設置一個變量,用來存儲用戶名和密碼信息。express

[html]  view plain  copy
 
  1. <on-start>  
  2.     <evaluate expression="initialFlowSetupAction" />  
  3. </on-start>  

整個登陸流程今後處開始,流程初始化initialFlowSetupAction的配置信息在/WEB-INF/cas-servlet.xml中。緩存

/WEB-INF/cas-servlet.xml部分代碼服務器

[html]  view plain  copy
 
  1. <bean id="initialFlowSetupAction" class="org.jasig.cas.web.flow.InitialFlowSetupAction"  
  2.         p:argumentExtractors-ref="argumentExtractors"  
  3.         p:warnCookieGenerator-ref="warnCookieGenerator"  
  4.         p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"/>  

其中argumentExtractors配置文件在/WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml中。cookie

/WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml部分代碼session

[html]  view plain  copy
 
  1. <bean  
  2.     id="casArgumentExtractor"  
  3.     class="org.jasig.cas.web.support.CasArgumentExtractor"  
  4.     p:httpClient-ref="noRedirectHttpClient"  
  5.     p:disableSingleSignOut="${slo.callbacks.disabled:false}" />  
  6.   
  7. <bean id="samlArgumentExtractor" class="org.jasig.cas.web.support.SamlArgumentExtractor"  
  8.     p:httpClient-ref="noRedirectHttpClient"  
  9.     p:disableSingleSignOut="${slo.callbacks.disabled:false}" />  
  10.       
  11. <util:list id="argumentExtractors">  
  12.     <ref bean="casArgumentExtractor" />  
  13.     <ref bean="samlArgumentExtractor" />  
  14. </util:list>  

其中warnCookieGenerator配置文件在/WEB-INF/spring-configuration/warnCookieGenerator.xml中。app

/WEB-INF/spring-configuration/warnCookieGenerator.xml部分代碼

[html]  view plain  copy
 
  1. <bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"  
  2.     p:cookieSecure="true"  
  3.     p:cookieMaxAge="-1"  
  4.     p:cookieName="CASPRIVACY"  
  5.     p:cookiePath="/cas" />  

其中ticketGrantingTicketCookieGenerator配置文件在/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml中。

/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml部分代碼

 

[html]  view plain  copy
 
  1. <bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"  
  2.     p:cookieSecure="false"  
  3.     p:cookieMaxAge="-1"  
  4.     p:cookieName="CASTGC"  
  5.     p:cookiePath="/cas" />  

 

初始化部分會調用InitialFlowSetupAction的doExecute方法,若是有特殊需求,能夠在此方法中增長相應的邏輯。若是但願單點登陸集成統一身份認證,那麼能夠在此處增長統一身份認證的邏輯。關於CAS單點登陸與統一身份認證的集成,我會單獨寫一篇。

InitialFlowSetupAction的doExecute方法

 

[java]  view plain  copy
 
  1. protected Event doExecute(final RequestContext context) throws Exception {  
  2.     final HttpServletRequest request = WebUtils.getHttpServletRequest(context);  
  3.     if (!this.pathPopulated) {  
  4.         final String contextPath = context.getExternalContext().getContextPath();  
  5.         final String cookiePath = StringUtils.hasText(contextPath) ? contextPath + "/" : "/";  
  6.         logger.info("Setting path for cookies to: " + cookiePath);  
  7.         this.warnCookieGenerator.setCookiePath(cookiePath);  
  8.         this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);  
  9.         this.pathPopulated = true;  
  10.     }  
  11.     //將TGT放在FlowScope做用域中  
  12.     context.getFlowScope().put(  
  13.         "ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));  
  14.     //將warnCookieValue放在FlowScope做用域中  
  15.     context.getFlowScope().put(  
  16.         "warnCookieValue", Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request)));  
  17.     //獲取service參數  
  18.     final Service service = WebUtils.getService(this.argumentExtractors, context);  
  19.   
  20.     if (service != null && logger.isDebugEnabled()) {  
  21.         logger.debug("Placing service in FlowScope: " + service.getId());  
  22.     }  
  23.     //將service放在FlowScope做用域中  
  24.     context.getFlowScope().put("service", service);  
  25.   
  26.     return result("success");  
  27. }  

InitialFlowSetupAction的doExecute要作的就是把ticketGrantingTicketId,warnCookieValue和service放到FlowScope的做用域中,以便在登陸流程中的state中進行判斷。初始化完成後,登陸流程流轉到第一個state(ticketGrantingTicketExistsCheck)。

 

 

[html]  view plain  copy
 
  1. <decision-state id="ticketGrantingTicketExistsCheck">  
  2.     <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" />  
  3. </decision-state>  

當咱們第一次訪問集成了CAS單點登陸的應用系統webapp1時(http://127.0.0.1:8090/webapp1/main.do),此時應用系統會跳轉到CAS單點登陸的服務器端(http://127.0.0.1:8081/cas-server/login?service=http://127.0.0.1:8090/webapp1/main.do)。此時,request的cookies中不存在CASTGC(TGT),所以FlowScope做用域中的ticketGrantingTicketId爲null,登陸流程流轉到第二個state(gatewayRequestCheck)。

 

 

[html]  view plain  copy
 
  1. <decision-state id="gatewayRequestCheck">  
  2.     <if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null"   
  3.         then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck" />  
  4. </decision-state>  

由於初始化時,儘管把service保存在了FlowScope做用域中,但request中的參數gateway不存在,登陸流程流轉到第三個state(serviceAuthorizationCheck)。

 

 

[html]  view plain  copy
 
  1. <action-state id="serviceAuthorizationCheck">  
  2.     <evaluate expression="serviceAuthorizationCheck"/>  
  3.     <transition to="generateLoginTicket"/>  
  4. </action-state>  

 

ServiceAuthorizationCheck的doExecute方法

 

[java]  view plain  copy
 
  1. protected Event doExecute(final RequestContext context) throws Exception {  
  2.     final Service service = WebUtils.getService(context);  
  3.     //No service == plain /login request. Return success indicating transition to the login form  
  4.     if(service == null) {  
  5.         return success();  
  6.     }  
  7.     final RegisteredService registeredService = this.servicesManager.findServiceBy(service);  
  8.   
  9.     if (registeredService == null) {  
  10.         logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not defined in the service registry.", service.getId());  
  11.         throw new UnauthorizedServiceException();  
  12.     }  
  13.     else if (!registeredService.isEnabled()) {  
  14.         logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not enabled in the service registry.", service.getId());  
  15.         throw new UnauthorizedServiceException();  
  16.     }  
  17.   
  18.     return success();  
  19. }  

 

ServiceAuthorizationCheck的doExecute方法,要作的就是判斷FlowScope做用域中是否存在service,若是service存在,查找service的註冊信息。登陸流程流轉到第四個state(generateLoginTicket)。

 

[html]  view plain  copy
 
  1. <action-state id="generateLoginTicket">  
  2.     <evaluate expression="generateLoginTicketAction.generate(flowRequestContext)" />  
  3.     <transition on="generated" to="viewLoginForm" />  
  4. </action-state>  

/WEB-INF/cas-servlet.xml部分代碼

[html]  view plain  copy
 
  1. <bean id="generateLoginTicketAction" class="org.jasig.cas.web.flow.GenerateLoginTicketAction"  
  2.     p:ticketIdGenerator-ref="loginTicketUniqueIdGenerator" />  

/WEB-INF/spring-configuration/uniqueIdGenerators.xml部分代碼

[html]  view plain  copy
 
  1. <bean id="loginTicketUniqueIdGenerator" class="org.jasig.cas.util.DefaultUniqueTicketIdGenerator">  
  2.     <constructor-arg  
  3.         index="0"  
  4.         type="int"  
  5.         value="30" />  
  6. </bean>  

 

DefaultUniqueTicketIdGenerator要作的就是生成以LT做爲前綴的loginTicket(例:LT-2-pfDmbEHfX2OkS0swLtDd7iDwmzlhsn)。注:LT只做爲登陸時使用的票據。

GenerateLoginTicketAction的generate方法

 

[java]  view plain  copy
 
  1. public final String generate(final RequestContext context) {  
  2.     //LT-2-pfDmbEHfX2OkS0swLtDd7iDwmzlhsn  
  3.     final String loginTicket = this.ticketIdGenerator.getNewTicketId(PREFIX);//生成loginTicket  
  4.     this.logger.debug("Generated login ticket " + loginTicket);  
  5.     WebUtils.putLoginTicket(context, loginTicket);//放到flowScope中  
  6.     return "generated";  
  7. }  

GenerateLoginTicketAction的generate要作的就是生成loginTicket,而且把loginTicket放到FlowScope做用域中。登陸流程流轉到第五個state(viewLoginForm)。

[html]  view plain  copy
 
  1. <view-state id="viewLoginForm" view="casLoginView" model="credentials">  
  2.     <binder>  
  3.         <binding property="username" />  
  4.         <binding property="password" />  
  5.     </binder>  
  6.     <on-entry>  
  7.         <set name="viewScope.commandName" value="'credentials'" />  
  8.     </on-entry>  
  9.           
  10.     <transition on="submit" bind="true" validate="true" to="realSubmit">  
  11.         <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />  
  12.     </transition>  
  13. </view-state>  

        至此,通過五個state的流轉,咱們完成了第一次訪問集成了單點登陸的應用系統,此時流轉到CAS單點登陸服務器端的登陸頁面/WEB-INF/jsp/ui/default/casLoginView.jsp。因爲casLoginView.jsp是CAS提供的默認登陸頁面,須要把此頁面修改爲咱們系統須要的登陸頁面,格式須要參考casLoginView.jsp。

注意,默認的登陸頁面中有lt、execution和_eventId三個隱藏參數,lt參數值就是在GenerateLoginTicketAction的generate方法中生成的loginTicket。

 

[html]  view plain  copy
 
  1. <input type="hidden" name="lt" value="${loginTicket}" />  
  2. <input type="hidden" name="execution" value="${flowExecutionKey}" />  
  3. <input type="hidden" name="_eventId" value="submit" />  

 

下面說一下CAS單點登陸服務器端的登陸驗證

當輸入用戶名和密碼,點擊登陸按鈕時,會執行AuthenticationViaFormAction的doBind方法。

[html]  view plain  copy
 
  1. <bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction"  
  2.     p:centralAuthenticationService-ref="centralAuthenticationService"  
  3.     p:warnCookieGenerator-ref="warnCookieGenerator" />  

 

AuthenticationViaFormAction的doBind方法

 

[java]  view plain  copy
 
  1. public final void doBind(final RequestContext context, final Credentials credentials) throws Exception {  
  2.     final HttpServletRequest request = WebUtils.getHttpServletRequest(context);  
  3.     //bean中沒有注入,這裏什麼也不作  
  4.     if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) {  
  5.         this.credentialsBinder.bind(request, credentials);  
  6.     }  
  7. }  

 

登陸流程流轉到第一個state(realSubmit),會執行AuthenticationViaFormAction的submit方法。

[html]  view plain  copy
 
  1. <action-state id="realSubmit">  
  2.     <evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" />  
  3.     <transition on="warn" to="warn" /><!-- 警告,轉向其餘站點前提示我 -->  
  4.     <transition on="success" to="sendTicketGrantingTicket" /><!-- 成功 -->  
  5.     <transition on="error" to="generateLoginTicket" /><!-- 錯誤 -->  
  6.     <transition on="accountDisabled" to="casAccountDisabledView" />  
  7.     <transition on="mustChangePassword" to="casMustChangePassView" />  
  8.     <transition on="accountLocked" to="casAccountLockedView" />  
  9.     <transition on="badHours" to="casBadHoursView" />  
  10.     <transition on="badWorkstation" to="casBadWorkstationView" />  
  11.     <transition on="passwordExpired" to="casExpiredPassView" />  
  12. </action-state>  

AuthenticationViaFormAction的submit方法

 

[java]  view plain  copy
 
  1. public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext)   
  2.     throws Exception {  
  3.     // Validate login ticket  
  4.     final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context);  
  5.     final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context);  
  6.     //判斷FlowScope和request中的loginTicket是否相同  
  7.     if (!authoritativeLoginTicket.equals(providedLoginTicket)) {  
  8.         this.logger.warn("Invalid login ticket " + providedLoginTicket);  
  9.         final String code = "INVALID_TICKET";  
  10.         messageContext.addMessage(new MessageBuilder().error().code(code).arg(providedLoginTicket).defaultText(code).build());  
  11.         return "error";  
  12.     }  
  13.     //requestScope和FlowScope中獲取TGT  
  14.     final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);  
  15.     //FlowScope中獲取service  
  16.     final Service service = WebUtils.getService(context);  
  17.     if (StringUtils.hasText(context.getRequestParameters().get("renew"))   
  18.             && ticketGrantingTicketId != null && service != null) {  
  19.   
  20.         try {  
  21.             final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(  
  22.                 ticketGrantingTicketId, service, credentials);  
  23.             WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);  
  24.             putWarnCookieIfRequestParameterPresent(context);  
  25.             return "warn";  
  26.         } catch (final TicketException e) {  
  27.             if (isCauseAuthenticationException(e)) {  
  28.                 populateErrorsInstance(e, messageContext);  
  29.                 return getAuthenticationExceptionEventId(e);  
  30.             }  
  31.                   
  32.             this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);  
  33.             if (logger.isDebugEnabled()) {  
  34.                 logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);  
  35.             }  
  36.         }  
  37.     }  
  38.   
  39.     try {  
  40.         //根據用戶憑證構造TGT,把TGT放到requestScope中,同時把TGT緩存到服務器的cache<ticketId,TGT>中  
  41.         WebUtils.putTicketGrantingTicketInRequestScope(context,   
  42.             this.centralAuthenticationService.createTicketGrantingTicket(credentials));  
  43.         putWarnCookieIfRequestParameterPresent(context);  
  44.         return "success";  
  45.     } catch (final TicketException e) {  
  46.         populateErrorsInstance(e, messageContext);  
  47.         if (isCauseAuthenticationException(e))  
  48.             return getAuthenticationExceptionEventId(e);  
  49.         return "error";  
  50.     }  
  51. }  

AuthenticationViaFormAction的submit要作的就是判斷FlowScope和request中的loginTicket是否相同。若是不一樣跳轉到錯誤頁面,若是相同,則根據用戶憑證生成TGT(登陸成功票據),並放到requestScope做用域中,同時把TGT緩存到服務器的cache<ticketId,TGT>中。登陸流程流轉到第二個state(sendTicketGrantingTicket)。

 

既然是登陸,那麼能夠在此方法中加入本身的業務邏輯,好比,能夠加入驗證碼的判斷,以及錯誤信息的提示,用戶名或者密碼錯誤,驗證碼錯誤等邏輯判斷。

[html]  view plain  copy
 
  1. <action-state id="sendTicketGrantingTicket">  
  2.     <evaluate expression="sendTicketGrantingTicketAction" />  
  3.     <transition to="serviceCheck" />  
  4. </action-state>  

SendTicketGrantingTicketAction的doExecute方法

 

[java]  view plain  copy
 
  1. protected Event doExecute(final RequestContext context) {  
  2.     //requestScope和FlowScope中獲取TGT  
  3.     final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);   
  4.     final String ticketGrantingTicketValueFromCookie = (String) context.getFlowScope().get("ticketGrantingTicketId");  
  5.           
  6.     if (ticketGrantingTicketId == null) {  
  7.         return success();  
  8.     }  
  9.     //response中添加TGC  
  10.     this.ticketGrantingTicketCookieGenerator.addCookie(WebUtils.getHttpServletRequest(context), WebUtils  
  11.         .getHttpServletResponse(context), ticketGrantingTicketId);  
  12.   
  13.     if (ticketGrantingTicketValueFromCookie != null && !ticketGrantingTicketId.equals(ticketGrantingTicketValueFromCookie)) {  
  14.         this.centralAuthenticationService  
  15.             .destroyTicketGrantingTicket(ticketGrantingTicketValueFromCookie);  
  16.     }  
  17.   
  18.     return success();  
  19. }  

 

SendTicketGrantingTicketAction的doExecute要作的是獲取TGT,並根據TGT生成cookie添加到response。登陸流程流轉到第三個state(serviceCheck)。

 

[html]  view plain  copy
 
  1. <decision-state id="serviceCheck">  
  2.     <if test="flowScope.service != null" then="generateServiceTicket" else="viewGenericLoginSuccess" />  
  3. </decision-state>  

因爲此時FlowScope中存在service(http://127.0.0.1:8081/cas-server/login?service=http://127.0.0.1:8090/webapp1/main.do),登陸流程流轉到第四個state(generateServiceTicket)。

[html]  view plain  copy
 
  1. <action-state id="generateServiceTicket">  
  2.     <evaluate expression="generateServiceTicketAction" />  
  3.     <transition on="success" to ="warn" />  
  4.     <transition on="error" to="generateLoginTicket" />  
  5.     <transition on="gateway" to="gatewayServicesManagementCheck" />  
  6. </action-state>  

GenerateServiceTicketAction的doExecute方法

[java]  view plain  copy
 
  1. protected Event doExecute(final RequestContext context) {  
  2.     //獲取service  
  3.     final Service service = WebUtils.getService(context);  
  4.     //獲取TGT  
  5.     final String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context);  
  6.   
  7.     try {  
  8.         //根據TGT和service生成service ticket(ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org)  
  9.         final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicket,  
  10.             service);  
  11.         //ST放到requestScope中  
  12.         WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);  
  13.         return success();  
  14.     } catch (final TicketException e) {  
  15.         if (isGatewayPresent(context)) {  
  16.             return result("gateway");  
  17.         }  
  18.     }  
  19.   
  20.     return error();  
  21. }  

GenerateServiceTicketAction的doExecute要作的是獲取service和TGT,並根據service和TGT生成以ST爲前綴的serviceTicket(例:ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org),並把serviceTicket放到requestScope中。登陸流程流轉到第五個state(warn)。

[html]  view plain  copy
 
  1. <decision-state id="warn">  
  2.     <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" />  
  3. </decision-state>  

因爲此時FlowScope中不存在warnCookieValue,登陸流程流轉到第六個state(redirect)。

 

[html]  view plain  copy
 
  1. <action-state id="redirect">  
  2.     <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)"   
  3.         result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" />  
  4.     <transition to="postRedirectDecision" />  
  5. </action-state>  

從requestScope中獲取serviceTicket,構造response對象,並把response放到requestScope中。登陸流程流轉到第七個state(postRedirectDecision)。

 

 

[html]  view plain  copy
 
  1. <decision-state id="postRedirectDecision">  
  2.     <if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView" />  
  3. </decision-state>  

因爲request請求(http://127.0.0.1:8081/cas-server/login?service=http://127.0.0.1:8090/webapp1/main.do)是get類型,登陸流程流轉到第八個state(redirectView)。

[html]  view plain  copy
 
  1. <end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" />  

此時流程以下:

 

  1. 跳轉到應用系統(http://127.0.0.1:8090/webapp1/main.do?ticket=ST-1-4hH2s5tzsMGCcToDvGCb-cas01.example.org)。
  2. 進入CAS客戶端的AuthenticationFilter過濾器,因爲session中獲取名爲「_const_cas_assertion_」的assertion對象不存在,可是request有ticket參數,因此進入到下一個過濾器。
  3. TicketValidationFilter過濾器的validate方法經過httpClient訪問CAS服務器端(http://127.0.0.1:8081/cas-server/serviceValidate?ticket=ST-1-4hH2s5tzsMGCcToDvGCb-cas01.example.org&service=http://127.0.0.1:8090/webapp1/main.do)驗證ticket是否正確,並返回assertion對象。
Assertion對象格式相似於
[html]  view plain  copy
 
  1. <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>  
  2.     <cas:authenticationSuccess>  
  3.         <cas:user>system</cas:user>  
  4.   
  5.     </cas:authenticationSuccess>  
  6. </cas:serviceResponse>  

訪問集成了CAS單點登陸的應用系統webapp2

當咱們第一次訪問集成了CAS單點登陸的應用系統webapp2時(http://127.0.0.1:8091/webapp2/main.do),此時應用系統會跳轉到CAS單點登陸的服務器端(http://127.0.0.1:8081/cas-server/login?service=http://127.0.0.1:8091/webapp2/main.do)。

InitialFlowSetupAction的doExecute初始化完成後,登陸流程流轉到第一個state(ticketGrantingTicketExistsCheck)。

 

[html]  view plain  copy
 
  1. <decision-state id="ticketGrantingTicketExistsCheck">  
  2.     <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" />  
  3. </decision-state>  

由於應用系統webapp1已經成功登陸,因此request的cookies中存在TGT,並保存到FlowScope中,登陸流程流轉到第二個state(hasServiceCheck)。

 

 

[html]  view plain  copy
 
  1. <decision-state id="hasServiceCheck">  
  2.     <if test="flowScope.service != null" then="renewRequestCheck" else="viewGenericLoginSuccess" />  
  3. </decision-state>  

FlowScope中存在service,登陸流程流轉到第三個state(renewRequestCheck)。

 

 

[html]  view plain  copy
 
  1. <decision-state id="renewRequestCheck">  
  2.     <if test="requestParameters.renew != '' and requestParameters.renew != null"   
  3.         then="serviceAuthorizationCheck" else="generateServiceTicket" />  
  4. </decision-state>  

request中不存在renew,登陸流程流轉到第四個state(generateServiceTicket)。

 

[html]  view plain  copy
 
  1. <action-state id="generateServiceTicket">  
  2.     <evaluate expression="generateServiceTicketAction" />  
  3.     <transition on="success" to ="warn" />  
  4.     <transition on="error" to="generateLoginTicket" />  
  5.     <transition on="gateway" to="gatewayServicesManagementCheck" />  
  6. </action-state>  

 

後續的流轉與應用系統webapp1相同,請參考前面webapp1的流轉。

 

http://blog.csdn.net/dovejing/article/details/44523545

相關文章
相關標籤/搜索