zoukankan      html  css  js  c++  java
  • CAS源码追踪系列三:cas-server端对请求的处理

    系列:

    上一篇,我们了解了AuthenticationFilter对请求的过滤,如果发现session中没有名为_const_cas_assertion_的assertion对象,而且request中也没有对应的ticket,那么就会跳转到统一登录页面。
    那这次我们就来看看cas-server如何处理统一登录(版本:cas-server-3.5.2)。

    InitialFlowSetupAction

    首先会进入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参数(即是都接入网关),如果没有则进入下一个校验:服务认证检查。

    ServiceAuthorizationCheck

    进入ServiceAuthorizationCheck,他做的操作如下:

    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的注册信息,看看是都存在和是否被禁用,如果不存在或者禁用了则会抛出未认证服务异常。

    然后生成LT为前缀的登录票据loginTicket并将其放到flowScope中。

    再然后就是进入登录页面。该页面中有三个隐藏参数:loginTicket、execution、_eventId

    AuthenticationViaFormAction

    当输入用户名和密码,点击登录按钮时,会执行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中。

    SendTicketGrantingTicketAction

    根据TGT生成cookie并将其添加到response返回给客户端。

    GenerateServiceTicketAction

    然后进行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的另一个应用系统时,同样跳转到cas-server,还是会先执行InitialFlowSetupAction的初始化,由于之前的系统已经登录过了,FlowScope中存在TGT,则检查FlowScope中是否存在service,如果存在则判断request中是否有renew参数,如果没有就生成serviceTicket,后续和上文流程一样。

    总结

    本文主要讲了请求跳转到cas-server之后的处理,至此对cas的单点登陆流程及原理有了更多的了解。

    参考资料:https://blog.csdn.net/dovejing/article/details/44523545

    平时的学习过程记录一下,没有那么高深,希望能帮到大家,与君共同进步。我是敲代码的小鲁班,喜欢的话给个推荐,点赞,关注吧。

  • 相关阅读:
    SQL CREATE DATABASE 语句
    SQL SELECT INTO 语句
    SQL UNION 和 UNION ALL 操作符
    复盘实战营一期毕业典礼----HHR计划----以太入门课--第一课
    抑郁研究所融资历程分享--以太一堂--直播课
    投资人分享答疑----HHR计划----以太直播课第三课
    重新理解《务实创业》---HHR计划--以太一堂第三课
    HHR计划---作业复盘-直播第三课
    电影推荐算法---HHR计划
    一堂优秀学员吕智钊分享----HHR计划----直播课第二课
  • 原文地址:https://www.cnblogs.com/xuxiaojian/p/9973866.html
Copyright © 2011-2022 走看看