接着上一次結束的地方 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)
- CAS 服務端登錄驗證流程(二):http://blog.csdn.net/pomer_huang/article/details/76862482
- CAS 服務端登錄驗證流程(四):http://blog.csdn.net/pomer_huang/article/details/76862521