目录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
平时的学习过程记录一下,没有那么高深,但愿能帮到你们,与君共同进步。我是敲代码的小鲁班,喜欢的话给个推荐,点赞,关注吧。