天天看点

CAS单点登录开源框架解读(三)--CAS单点登录服务端认证之loginFlowRegistry流程如何进入loginFlowRegistry流程

如何进入loginFlowRegistry流程

1、如何从”/”变为”/login”

在4.2.6源码包中的cas-server-webapp工程下,webapp\WEB-INF\目录下有个web.xml文件,其中有如下的配置。

*web.xml:*
<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
           
  • 1
  • 2
  • 3
  • 4

由”/”转化为”index.jsp”页面,index.jsp页面在webapp\目录下。

*index.jsp:*
<%@ page language="java"  session="false" %>
<%
final String queryString = request.getQueryString();
final String url = request.getContextPath() + "/login" + (queryString != null ? '?' + queryString : "");
response.sendRedirect(response.encodeURL(url));%>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果对于是CAS客户端请求过来的地址,会被CAS客户端的过滤器进行封装后形成请求CAS单点登录服务端的重定向地址为如下格式:http://localhost:8080/cas/login?service=http%3A%2F%2Flocalhost%3A8088%2Fcas-client%2F。

但是在本文进行源码解读时,直接访问的是CAS单点登录服务端的地址,因此index.jsp中的queryString的值为空,所以会直接请求跳转到http://localhost:8080/cas/login地址。

2、如何映射login到CAS中,通过servlet-mapping到cas

在cas-server-webapp工程下,webapp\WEB-INF\目录下的web.xml文件。通过最基础的servlet进行相关的映射。

*web.xml:*
<servlet-mapping>
        <servlet-name>cas</servlet-name>
        <url-pattern>/login</url-pattern>
</servlet-mapping>
           
  • 1
  • 2
  • 3
  • 4
  • 5

3、CAS把映射交给Spring的DispatcherServlet处理

我们知道CAS服务端是采用了Spring web flow来进行流程的处理,那么如何将servlet和Spring进行结合的。还是看web.xml配置文件。

*web.xml:*
<servlet>
        <servlet-name>cas</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- Load the child application context. Start with the default, then modules, then overlays. -->
            <param-value>/WEB-INF/cas-servlet.xml,classpath*:/META-INF/cas-servlet-*.xml,/WEB-INF/cas-servlet-*.xml</param-value>
        </init-param>
        <init-param>
            <param-name>publishContext</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

请注意配置文件中的/WEB-INF/cas-servlet.xml这个配置文件。

4、解析cas-servlet.xml相关配置

我们目前关心的还是登录流程到底是怎么进行的,那么请看下面这段配置。

*cas-servlet.xml:*
<!-- login webflow configuration -->
    <bean id="loginFlowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
          p:flowRegistry-ref="loginFlowRegistry" p:order="2">
        <property name="interceptors">
            <array value-type="org.springframework.web.servlet.HandlerInterceptor">
                <ref bean="localeChangeInterceptor"/>
                <ref bean="authenticationThrottle"/>
            </array>
        </property>
    </bean>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从注释可以知道这是登录流程的入口配置,定义流程句柄bean为loginFlowHandlerMapping,流程为loginFlowRegistry。那么这个loginFlowRegistry属性是在哪里进行了定义呢?这个其实是在Spring相关的配置文件中进行配置的,那么我们继续看是如何解析Spring的配置文件的。

还是在web.xml文件中,我们看一下Spring的配置文件加载了哪一些。

*web.xml:*
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-configuration/*.xml
            /WEB-INF/deployerConfigContext.xml
            <!-- this enables extensions and addons to contribute to overall CAS' application context
                 by loading spring context files from classpath i.e. found in classpath jars, etc. -->
            classpath*:/META-INF/spring/*.xml
        </param-value>
    </context-param>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从上面的配置信息可以看出spring配置在/WEB-INF/spring-configuration/*.xml中。那么loginFlowRegistry这个是在哪里定义的呢,我们可以猜测这个应该是和webflow相关的,所以可以在Spring的配置文件下找到webflowContext.xml文件。

5、解析webflowContext.xml上下文配置

webflowContext.xml在/WEB-INF/spring-configuration/目录下。

*webflowContext.xml:*
<webflow:flow-registry id="loginFlowRegistry" flow-builder-services="builder" base-path="/WEB-INF/webflow">
        <webflow:flow-location-pattern value="/login/*-webflow.xml"/>
</webflow:flow-registry>
           

< webflow:flow-registry id=“logoutFlowExecutor” flow-registry=“logoutFlowRegistry”>

<webflow:flow-execution-attributes>

<webflow:always-redirect-on-pause value=“false”/>

<webflow:redirect-in-same-state value=“false”/>

</webflow:flow-execution-attributes>

</webflow:flow-registry>

<webflow:flow-registry id=“logoutFlowRegistry” flow-builder-services=“builder” base-path="/WEB-INF/webflow">

<webflow:flow-location-pattern value="/logout/*-webflow.xml"/>

</webflow:flow-registry>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

定义了cas-servlet.xml 里登录Mapping对应的loginFlowRegistry,其流程配置文件/login/-webflow.xml即login-webflow.xml。同时还配置了退出流程logoutFlowRegistry,其配置文件/logout/-webflow.xml即logout-webflow.xml。

6、解析login-webflow.xml配置文件的初始化登录action

启动登录流程的是initialFlowSetupAction这个类。(具体解析login-webflow.xml文件请查看下一章节)

*login-webflow.xml:*
           

<var name=“credential” class=“org.jasig.cas.authentication.UsernamePasswordCredential”/>

<span class="token comment">&lt;!--
&lt;var name="credential" class="org.jasig.cas.authentication.RememberMeUsernamePasswordCredential" /&gt;
--&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>on-start</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>evaluate</span> <span class="token attr-name">expression</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>initialFlowSetupAction<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>on-start</span><span class="token punctuation">&gt;</span></span>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

7、登录流程处理HandlerAdapter

找到了登录流程,那么是如何通过/login进入此登录流程的呢?下面我们还是看/WEB-INF/cas-servlet.xml这个配置文件。

*cas-servlet.xml:*
  <bean id="loginHandlerAdapter" class="org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter"
          p:supportedFlowId="login" p:flowExecutor-ref="loginFlowExecutor" p:flowUrlHandler-ref="loginFlowUrlHandler"/>
            
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowUrlHandler<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.jasig.cas.web.flow.CasDefaultFlowUrlHandler<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowExecutor<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.webflow.executor.FlowExecutorImpl<span class="token punctuation">"</span></span>
      <span class="token attr-name"><span class="token namespace">c:</span>definitionLocator-ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowRegistry<span class="token punctuation">"</span></span>
      <span class="token attr-name"><span class="token namespace">c:</span>executionFactory-ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowExecutionFactory<span class="token punctuation">"</span></span>
      <span class="token attr-name"><span class="token namespace">c:</span>executionRepository-ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowExecutionRepository<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowExecutionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.webflow.engine.impl.FlowExecutionImplFactory<span class="token punctuation">"</span></span>
      <span class="token attr-name"><span class="token namespace">p:</span>executionKeyFactory-ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginFlowExecutionRepository<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

请注意loginHandlerAdapter这个配置的bean,其中的属性有supportedFlowId的值为“login”,同时属性flowExecutor-ref的引用值为loginFlowExecutor。再看loginFlowExecutor这个bean中所配置的登录流程属性引用值就是我们webflow上下文配置中的loginFlowRegistry这个属性。

因此我们来看一下loginHandlerAdapter这个bean对应的类为org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter所起的作用,是如何来处理登录动作的。先来看一下这个类的父类org.springframework.webflow.mvc.servlet.FlowHandlerAdapter,这个是Springmvc中的一个类。

public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAdapter, InitializingBean {
           
  • 1

FlowHandlerAdapter实现接口HandlerAdapter,而SelectiveFlowHandlerAdapter继承自FlowHandlerAdapter(如下)。SelectiveFlowHandlerAdapter类在cas-server-webapp-actions模块下的org.jasig.cas.web.flow包下。

因此Spring的DispatcherServlet找到要处理的handleAdapter是SelectiveFlowHandlerAdapte。而且根据地址http://localhost:8080/cas/login?service=XXX,得到handler的flowId=“login”,即流程:loginFlowRegistry,然后进入下面的handle方法,开始调取流程:

*org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter.java*
public class SelectiveFlowHandlerAdapter extends FlowHandlerAdapter {
//此handle方法在父类中FlowHandlerAdapter中
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		FlowHandler flowHandler = (FlowHandler) handler;
		checkAndPrepare(request, response, false);
//注意flowUrlHandler通过cas-servlet.xml中的配置类为//org.jasig.cas.web.flow.CasDefaultFlowUrlHandler,通过request来获取名称为
//“execution”的参数值,也就是flowExecutionKey。
		String flowExecutionKey = flowUrlHandler.getFlowExecutionKey(request);
//对于第一请求跳转到登录页面,这个值为空,执行else中的代码
		if (flowExecutionKey != null) {
			try {
				ServletExternalContext context = createServletExternalContext(request, response);
				FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
				handleFlowExecutionResult(result, context, request, response, flowHandler);
			} catch (FlowException e) {
				handleFlowException(e, request, response, flowHandler);
			}
		} else {
			try {
//第一次访问,在配置文件中配置的flowId为“login”
				String flowId = getFlowId(flowHandler, request);
				MutableAttributeMap<Object> input = getInputMap(flowHandler, request);
				ServletExternalContext context = createServletExternalContext(request, response);
//注意这里是最关键的点,加载登录流程的起始处,也就是通过这里把login-webflow.xml中//相关配置的流程走一遍。
				FlowExecutionResult result = flowExecutor.launchExecution(flowId, input, context);
				handleFlowExecutionResult(result, context, request, response, flowHandler);
			} catch (FlowException e) {
				handleFlowException(e, request, response, flowHandler);
			}
		}
		return null;
	}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

如何进入loginFlowRegistry流程

1、如何从”/”变为”/login”

在4.2.6源码包中的cas-server-webapp工程下,webapp\WEB-INF\目录下有个web.xml文件,其中有如下的配置。

*web.xml:*
<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
           
  • 1
  • 2
  • 3
  • 4

由”/”转化为”index.jsp”页面,index.jsp页面在webapp\目录下。

*index.jsp:*
<%@ page language="java"  session="false" %>
<%
final String queryString = request.getQueryString();
final String url = request.getContextPath() + "/login" + (queryString != null ? '?' + queryString : "");
response.sendRedirect(response.encodeURL(url));%>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果对于是CAS客户端请求过来的地址,会被CAS客户端的过滤器进行封装后形成请求CAS单点登录服务端的重定向地址为如下格式:http://localhost:8080/cas/login?service=http%3A%2F%2Flocalhost%3A8088%2Fcas-client%2F。

但是在本文进行源码解读时,直接访问的是CAS单点登录服务端的地址,因此index.jsp中的queryString的值为空,所以会直接请求跳转到http://localhost:8080/cas/login地址。

2、如何映射login到CAS中,通过servlet-mapping到cas

在cas-server-webapp工程下,webapp\WEB-INF\目录下的web.xml文件。通过最基础的servlet进行相关的映射。

*web.xml:*
<servlet-mapping>
        <servlet-name>cas</servlet-name>
        <url-pattern>/login</url-pattern>
</servlet-mapping>
           
  • 1
  • 2
  • 3
  • 4
  • 5

3、CAS把映射交给Spring的DispatcherServlet处理

我们知道CAS服务端是采用了Spring web flow来进行流程的处理,那么如何将servlet和Spring进行结合的。还是看web.xml配置文件。

*web.xml:*
<servlet>
        <servlet-name>cas</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- Load the child application context. Start with the default, then modules, then overlays. -->
            <param-value>/WEB-INF/cas-servlet.xml,classpath*:/META-INF/cas-servlet-*.xml,/WEB-INF/cas-servlet-*.xml</param-value>
        </init-param>
        <init-param>
            <param-name>publishContext</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

请注意配置文件中的/WEB-INF/cas-servlet.xml这个配置文件。

4、解析cas-servlet.xml相关配置

我们目前关心的还是登录流程到底是怎么进行的,那么请看下面这段配置。

*cas-servlet.xml:*
<!-- login webflow configuration -->
    <bean id="loginFlowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
          p:flowRegistry-ref="loginFlowRegistry" p:order="2">
        <property name="interceptors">
            <array value-type="org.springframework.web.servlet.HandlerInterceptor">
                <ref bean="localeChangeInterceptor"/>
                <ref bean="authenticationThrottle"/>
            </array>
        </property>
    </bean>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从注释可以知道这是登录流程的入口配置,定义流程句柄bean为loginFlowHandlerMapping,流程为loginFlowRegistry。那么这个loginFlowRegistry属性是在哪里进行了定义呢?这个其实是在Spring相关的配置文件中进行配置的,那么我们继续看是如何解析Spring的配置文件的。

还是在web.xml文件中,我们看一下Spring的配置文件加载了哪一些。

*web.xml:*
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-configuration/*.xml
            /WEB-INF/deployerConfigContext.xml
            <!-- this enables extensions and addons to contribute to overall CAS' application context
                 by loading spring context files from classpath i.e. found in classpath jars, etc. -->
            classpath*:/META-INF/spring/*.xml
        </param-value>
    </context-param>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从上面的配置信息可以看出spring配置在/WEB-INF/spring-configuration/*.xml中。那么loginFlowRegistry这个是在哪里定义的呢,我们可以猜测这个应该是和webflow相关的,所以可以在Spring的配置文件下找到webflowContext.xml文件。

5、解析webflowContext.xml上下文配置

webflowContext.xml在/WEB-INF/spring-configuration/目录下。

*webflowContext.xml:*
<webflow:flow-registry id="loginFlowRegistry" flow-builder-services="builder" base-path="/WEB-INF/webflow">
        <webflow:flow-location-pattern value="/login/*-webflow.xml"/>
</webflow:flow-registry>
           
cas

继续阅读