目錄html
系列:java
上一篇,咱們瞭解了AuthenticationFilter對請求的過濾,若是發現session中沒有名爲_const_cas_assertion_的assertion對象,並且request中也沒有對應的ticket,那麼就會跳轉到統一登陸頁面。
那此次咱們就來看看cas-server如何處理統一登陸(版本:cas-server-3.5.2)。web
首先會進入InitialFlowSetupAction,他所作的操做以下:緩存
protected Event doExecute(final RequestContext context)throws Exception { final HttpServletRequest request = WebUtils.getHttpServletRequest(context); if (!this.pathPopulated) { final String contextPath = context.getExternalContext().getContextPath(); final String cookiePath = StringUtils.hasText(contextPath) ? contextPath + "/" : "/"; logger.info("Setting path for cookies to: " + cookiePath); this.warnCookieGenerator.setCookiePath(cookiePath); this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath); this.pathPopulated = true; } //將TGT放在FlowScope做用域中 context.getFlowScope().put( "ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request)); //將warnCookieValue放在FlowScope做用域中 context.getFlowScope().put( "warnCookieValue", Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request))); //獲取service參數 final Service service = WebUtils.getService(this.argumentExtractors, context); if (service != null && logger.isDebugEnabled()) { logger.debug("Placing service in FlowScope: " + service.getId()); } //將service放在FlowScope做用域中 context.getFlowScope().put("service", service); return result("success"); }
上面主要作的就是把ticketGrantingTicketId,warnCookieValue和service放到FlowScope的做用域中。服務器
接下來須要作校驗:TicketGrantingTicket是都否存在。
若是不存在(首次登陸確定不存在)就會到下一個校驗:request中是否有gateway參數(便是都接入網關),若是沒有則進入下一個校驗:服務認證檢查。cookie
進入ServiceAuthorizationCheck,他作的操做以下:session
protected Event doExecute(final RequestContext context)throws Exception { final Service service = WebUtils.getService(context); //No service == plain /login request. Return success indicating transition to the login form if(service == null) { return success(); } final RegisteredService registeredService = this.servicesManager.findServiceBy(service); if (registeredService == null) { logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not defined in the service registry.", service.getId()); thrownew UnauthorizedServiceException(); } elseif (!registeredService.isEnabled()) { logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not enabled in the service registry.", service.getId()); thrownew UnauthorizedServiceException(); } return success(); }
主要作的就是判斷FlowScope做用域中是否存在請求指定的service,若是service存在,查找service的註冊信息,看看是都存在和是否被禁用,若是不存在或者禁用了則會拋出未認證服務異常。app
而後生成LT爲前綴的登陸票據loginTicket並將其放到flowScope中。webapp
再而後就是進入登陸頁面。該頁面中有三個隱藏參數:loginTicket、execution、_eventIdide
當輸入用戶名和密碼,點擊登陸按鈕時,會執行AuthenticationViaFormAction的doBind方法進行身份綁定。
publicfinalvoiddoBind(final RequestContext context, final Credentials credentials)throws Exception { final HttpServletRequest request = WebUtils.getHttpServletRequest(context); //bean中沒有注入,這裏什麼也不作 if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) { this.credentialsBinder.bind(request, credentials); } }
而後就會執行sumbit方法進行真正的表單提交。
publicfinal String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception { // Validate login ticket final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context); final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context); //判斷FlowScope和request中的loginTicket是否相同 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"; } //FlowScope中獲取TGT final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context); //FlowScope中獲取service final Service service = WebUtils.getService(context); 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 { //根據用戶憑證構造TGT,把TGT放到requestScope中,同時把TGT緩存到服務器的cache中 WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials)); putWarnCookieIfRequestParameterPresent(context); return"success"; } catch (final TicketException e) { populateErrorsInstance(e, messageContext); if (isCauseAuthenticationException(e)) return getAuthenticationExceptionEventId(e); return"error"; } }
主要作的就是判斷FlowScope和request中的loginTicket是否相同。若是不一樣跳轉到錯誤頁面,若是相同,則根據用戶憑證生成TGT(登陸成功票據),並放到requestScope做用域中,同時把TGT緩存到服務器的cache中。
根據TGT生成cookie並將其添加到response返回給客戶端。
而後進行service檢查,判斷FlowScope中是否存在service,若是存在(相似於http://127.0.0.1:8081/cas-server/login?service=http://127.0.0.1:8090/webapp1/main.do)就去調用doExecute方法生成服務票據ServiceTicket。
protected Event doExecute(final RequestContext context){ //獲取service final Service service = WebUtils.getService(context); //獲取TGT final String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context); try { //根據TGT和service生成service ticket(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(); }
要作的是獲取service和TGT,並根據service和TGT生成以ST爲前綴的serviceTicket(例:ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org),並把serviceTicket放到requestScope中
而後從requestScope中獲取serviceTicket,構造response對象,並把response放到requestScope中。
最後重定向到應用系統。
此時流程以下:
跳轉到應用系統(http://127.0.0.1:8090/webapp1/main.do?ticket=ST-1-4hH2s5tzsMGCcToDvGCb-cas01.example.org)。
進入CAS客戶端的AuthenticationFilter過濾器,因爲session中獲取名爲「const_cas_assertion」的assertion對象不存在,可是request有ticket參數,因此進入到下一個過濾器。
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對象。
當第一次訪問接入cas的另外一個應用系統時,一樣跳轉到cas-server,仍是會先執行InitialFlowSetupAction的初始化,因爲以前的系統已經登陸過了,FlowScope中存在TGT,則檢查FlowScope中是否存在service,若是存在則判斷request中是否有renew參數,若是沒有就生成serviceTicket,後續和上文流程同樣。
本文主要講了請求跳轉到cas-server以後的處理,至此對cas的單點登錄流程及原理有了更多的瞭解。
參考資料:https://blog.csdn.net/dovejing/article/details/44523545
平時的學習過程記錄一下,沒有那麼高深,但願能幫到你們,與君共同進步。我是敲代碼的小魯班,喜歡的話給個推薦,點贊,關注吧。