cas 解读

前期准备:

1.cas-server-3.5.2-release.zip

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包。

web.xml部分代码

[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. <servlet-mapping>
  11. <servlet-name>cas</servlet-name>
  12. <url-pattern>/login</url-pattern>
  13. </servlet-mapping>
  14. <servlet-mapping>
  15. <servlet-name>cas</servlet-name>
  16. <url-pattern>/logout</url-pattern>
  17. </servlet-mapping>
  18. <servlet-mapping>
  19. <servlet-name>cas</servlet-name>
  20. <url-pattern>/validate</url-pattern>
  21. </servlet-mapping>
  22. <servlet-mapping>
  23. <servlet-name>cas</servlet-name>
  24. <url-pattern>/serviceValidate</url-pattern>
  25. </servlet-mapping>
  26. <servlet-mapping>
  27. <servlet-name>cas</servlet-name>
  28. <url-pattern>/samlValidate</url-pattern>
  29. </servlet-mapping>
  30. <servlet-mapping>
  31. <servlet-name>cas</servlet-name>
  32. <url-pattern>/proxy</url-pattern>
  33. </servlet-mapping>
  34. <servlet-mapping>
  35. <servlet-name>cas</servlet-name>
  36. <url-pattern>/proxyValidate</url-pattern>
  37. </servlet-mapping>
  38. <servlet-mapping>
  39. <servlet-name>cas</servlet-name>
  40. <url-pattern>/CentralAuthenticationService</url-pattern>
  41. </servlet-mapping>
  42. <servlet-mapping>
  43. <servlet-name>cas</servlet-name>
  44. <url-pattern>/services/add.html</url-pattern>
  45. </servlet-mapping>
  46. <servlet-mapping>
  47. <servlet-name>cas</servlet-name>
  48. <url-pattern>/services/viewStatistics.html</url-pattern>
  49. </servlet-mapping>
  50. <servlet-mapping>
  51. <servlet-name>cas</servlet-name>
  52. <url-pattern>/services/logout.html</url-pattern>
  53. </servlet-mapping>
  54. <servlet-mapping>
  55. <servlet-name>cas</servlet-name>
  56. <url-pattern>/services/loggedOut.html</url-pattern>
  57. </servlet-mapping>
  58. <servlet-mapping>
  59. <servlet-name>cas</servlet-name>
  60. <url-pattern>/services/manage.html</url-pattern>
  61. </servlet-mapping>
  62. <servlet-mapping>
  63. <servlet-name>cas</servlet-name>
  64. <url-pattern>/services/edit.html</url-pattern>
  65. </servlet-mapping>
  66. <servlet-mapping>
  67. <servlet-name>cas</servlet-name>
  68. <url-pattern>/openid/*</url-pattern>
  69. </servlet-mapping>
  70. <servlet-mapping>
  71. <servlet-name>cas</servlet-name>
  72. <url-pattern>/services/deleteRegisteredService.html</url-pattern>
  73. </servlet-mapping>
  74. <servlet-mapping>
  75. <servlet-name>cas</servlet-name>
  76. <url-pattern>/services/updateRegisteredServiceEvaluationOrder.html</url-pattern>
  77. </servlet-mapping>
  78. <servlet-mapping>
  79. <servlet-name>cas</servlet-name>
  80. <url-pattern>/status</url-pattern>
  81. </servlet-mapping>
  82. <servlet-mapping>
  83. <servlet-name>cas</servlet-name>
  84. <url-pattern>/authorizationFailure.html</url-pattern>
  85. </servlet-mapping>
  86. <servlet-mapping>
  87. <servlet-name>cas</servlet-name>
  88. <url-pattern>/403.html</url-pattern>
  89. </servlet-mapping>
  90. <servlet-mapping>
  91. <servlet-name>cas</servlet-name>
  92. <url-pattern>/error</url-pattern>
  93. </servlet-mapping>
  94. <servlet-mapping>
  95. <servlet-name>cas</servlet-name>
  96. <url-pattern>/authcode</url-pattern>
  97. </servlet-mapping>

访问集成了CAS单点登录的应用系统webapp1

下面讲一下CAS单点登录服务器端的登录流程,流程的配置在/WEB-INF/login-webflow.xml文件中。

/WEB-INF/login-webflow.xml部分代码

[html] view plain copy

  1. <var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />

首先,设置一个变量,用来存储用户名和密码信息。

[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中。

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

[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. <bean id="samlArgumentExtractor" class="org.jasig.cas.web.support.SamlArgumentExtractor"
  7. p:httpClient-ref="noRedirectHttpClient"
  8. p:disableSingleSignOut="${slo.callbacks.disabled:false}" />
  9. <util:list id="argumentExtractors">
  10. <ref bean="casArgumentExtractor" />
  11. <ref bean="samlArgumentExtractor" />
  12. </util:list>

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

/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. if (service != null && logger.isDebugEnabled()) {
  20. logger.debug("Placing service in FlowScope: " + service.getId());
  21. }
  22. //将service放在FlowScope作用域中
  23. context.getFlowScope().put("service", service);
  24. return result("success");
  25. }

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. if (registeredService == null) {
  9. logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not defined in the service registry.", service.getId());
  10. throw new UnauthorizedServiceException();
  11. }
  12. else if (!registeredService.isEnabled()) {
  13. logger.warn("Unauthorized Service Access for Service: [ {} ] - service is not enabled in the service registry.", service.getId());
  14. throw new UnauthorizedServiceException();
  15. }
  16. return success();
  17. }

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. <transition on="submit" bind="true" validate="true" to="realSubmit">
  10. <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
  11. </transition>
  12. </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. try {
  20. final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(
  21. ticketGrantingTicketId, service, credentials);
  22. WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
  23. putWarnCookieIfRequestParameterPresent(context);
  24. return "warn";
  25. } catch (final TicketException e) {
  26. if (isCauseAuthenticationException(e)) {
  27. populateErrorsInstance(e, messageContext);
  28. return getAuthenticationExceptionEventId(e);
  29. }
  30. this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
  31. if (logger.isDebugEnabled()) {
  32. logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);
  33. }
  34. }
  35. }
  36. try {
  37. //根据用户凭证构造TGT,把TGT放到requestScope中,同时把TGT缓存到服务器的cache<ticketId,TGT>中
  38. WebUtils.putTicketGrantingTicketInRequestScope(context,
  39. this.centralAuthenticationService.createTicketGrantingTicket(credentials));
  40. putWarnCookieIfRequestParameterPresent(context);
  41. return "success";
  42. } catch (final TicketException e) {
  43. populateErrorsInstance(e, messageContext);
  44. if (isCauseAuthenticationException(e))
  45. return getAuthenticationExceptionEventId(e);
  46. return "error";
  47. }
  48. }

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. if (ticketGrantingTicketId == null) {
  6. return success();
  7. }
  8. //response中添加TGC
  9. this.ticketGrantingTicketCookieGenerator.addCookie(WebUtils.getHttpServletRequest(context), WebUtils
  10. .getHttpServletResponse(context), ticketGrantingTicketId);
  11. if (ticketGrantingTicketValueFromCookie != null && !ticketGrantingTicketId.equals(ticketGrantingTicketValueFromCookie)) {
  12. this.centralAuthenticationService
  13. .destroyTicketGrantingTicket(ticketGrantingTicketValueFromCookie);
  14. }
  15. return success();
  16. }

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. try {
  7. //根据TGT和service生成service ticket(ST-2-97kwhcdrBW97ynpBbZH5-cas01.example.org)
  8. final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicket,
  9. service);
  10. //ST放到requestScope中
  11. WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
  12. return success();
  13. } catch (final TicketException e) {
  14. if (isGatewayPresent(context)) {
  15. return result("gateway");
  16. }
  17. }
  18. return error();
  19. }

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. </cas:authenticationSuccess>
  5. </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

时间: 2024-08-11 08:37:40

cas 解读的相关文章

201709021工作日记--CAS解读

CAS主要参考博文:classtag  http://www.jianshu.com/p/473e14d5ab2d CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术 .Compare and Swap, 翻译成比较并交换. 简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值. java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性

源代码解读Cas实现单点登出(single sign out)功能实现原理

关于Cas实现单点登入(single sing on)功能的文章在网上介绍的比较多,想必大家多多少少都已经有所了解,在此就不再做具体介绍.如果不清楚的,那只能等我把single sign on这块整理出来后再了解了.当然去cas官方网站也是有很多的文章进行介绍.cas官网http://www.ja-sig.org/products/cas/. ok,现在开始本文的重点内容讲解,先来了解一下cas 实现single sign 的原理,如图所示: 登出原理图 从第一张图中,当一个web浏览器登录到应

源代码解读Cas实现单点登出(single sign out)功能实现原理--转

关于Cas实现单点登入(single sing on)功能的文章在网上介绍的比较多,想必大家多多少少都已经有所了解,在此就不再做具体介绍.如果不清楚的,那只能等我把single sign on这块整理出来后再了解了.当然去cas官方网站也是有很多的文章进行介绍.cas官网http://www.ja-sig.org/products/cas/. ok,现在开始本文的重点内容讲解,先来了解一下cas 实现single sign out的原理,如图所示: 图一                     

Ehcache详细解读

Ehcache详细解读 Ehcache  是现在最流行的纯Java开源缓存框架,配置简单.结构清晰.功能强大,最初知道它,是从Hibernate的缓存开始的.网上中文的EhCache材料以简单介绍和配置方法居多,如果你有这方面的问题,请自行google:对于API,官网上介绍已经非常清楚,请参见官网:但是很少见到特性说明和对实现原理的分析,因此在这篇文章里面,我会详细介绍和分析EhCache的特性,加上一些自己的理解和思考,希望对缓存感兴趣的朋友有所收获. 一.特性一览 ,来自官网,简单翻译一下

MemCache超详细解读

MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度.MemCaChe是一个存储键值对的HashMap,在内存中对任意的数据(比如字符串.对象等)所使用的key-value存储,数据可以来自数据库调用.API调用,或者页面渲染的结果.MemCache设计理念就是小而强大,它简单的设计促进了快速部署.易于开发并解决面对大规模的数据缓存的

【转】Ehcache详细解读

Ehcache 是现在最流行的纯Java开源缓存框架,配置简单.结构清晰.功能强大,最初知道它,是从Hibernate的缓存开始的.网上中文的EhCache材料 以简单介绍和配置方法居多,如果你有这方面的问题,请自行google:对于API,官网上介绍已经非常清楚,请参见官网:但是很少见到特性说明和对实现 原理的分析,因此在这篇文章里面,我会详细介绍和分析EhCache的特性,加上一些自己的理解和思考,希望对缓存感兴趣的朋友有所收获. 一.特性一览,来自官网,简单翻译一下: 1.快速轻量过去几年

Redis核心解读(转)

原文:Redis核心解读 Redis是知名的键值数据库,它广泛用于缓存系统.关于Redis的信息已经不用我多介绍了.这个系统的Redis文章主要从另外一个角度关注,Redis作为一个开源项目,短短2W行代码包含了一个健壮的服务器端软件的必需,我们从Redis中可以学习C语言项目的编程风格.范式,学习类Unix下的系统编程,还有对于一个常驻服务的健壮性考虑等等. 对于一个C语言的初学者来说,学习一个类似Redis这样不大不小的项目是非常好的选择.Redis既没有Nginx深入性能细节的晦涩编码方式

Ehcache详细解读(转)

Ehcache 是现在最流行的纯Java开源缓存框架,配置简单.结构清晰.功能强大,最初知道它,是从Hibernate的缓存开始的.网上中文的EhCache材料 以简单介绍和配置方法居多,如果你有这方面的问题,请自行google:对于API,官网上介绍已经非常清楚,请参见官网:但是很少见到特性说明和对实现 原理的分析,因此在这篇文章里面,我会详细介绍和分析EhCache的特性,加上一些自己的理解和思考,希望对缓存感兴趣的朋友有所收获. 一.特性一览,来自官网,简单翻译一下: 1.快速轻量过去几年

[转载] etcd全方位解读

原文: http://www.infoq.com/cn/articles/etcd-interpretation-application-scenario-implement-principle etcd使用了更易懂的raft数据一致性协议, 比paxos容易理解的多. 本文对etcd的使用场景和原理做了非常详细的描写, 是学习etcd不可多得的好材料. etcd:从应用场景到实现原理的全方位解读 作者 孙健波 发布于 2015年1月30日 | 讨论 分享到:微博微信FacebookTwitte