07 January 2016

Understanding Spring Web-Flow in Apache Fediz - Part 1

When I started to work with Apache Fediz, most of the actions looked like magic to me, because I was not able to understand how Spring Security and Spring Web Flow have been used in Apache Fediz. After several hours of learning and investigation I finally understood how all this works together.

In this post I would like to share with you from what I understood of how Fediz works internally.
When you take a look inside the WEB-INF/web.xml you will find three URL mappings:
<servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/services/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>idp</servlet-name>
  <url-pattern>/federation</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>metadata</servlet-name>
  <url-pattern>/FederationMetadata/2007-06/FederationMetadata.xml</url-pattern>
</servlet-mapping>
  • /services/* provides access to all REST services for updating the IDP configuration
  • /FederationMetadata/2007-06/FederationMetadata.xml provides access to the generated IDP metadata document which is usually used in application wizards to setup the correct IDP configuration
  • /federation provides access to the configured Spring Web-Flow

If you take a look inside the WEB-INF/web.xml you will see that the /federation URL is linked with org.springframework.web.servlet.DispatcherServlet. The initialization of the Spring Flow however takes place within the WEB-INF/idp-servlet.xml.
So how is the DispatcherServlet linked to the idp-servlet.xml?
The key to the answer is Spring MVC which uses Convention over Configuration (CoC). Spring will search for a file with the same name as the servlet-name and ending with "-servlet.xml". To apply this knowledge you can see that the /federation URL is mapped to the servlet name idp and thus the default configuration spring file which will be loaded from the DispatcherServlet is idp-servlet.xml

Spring Configuration

Within the idp-servlet.xml you will find a spring configuration to setup the Spring Web Flow.
First Spring will do a component scan to instantiate and autowire all beans with a @Component annotation located within the beans package. These beans usually provide specific actions which are executed within the flows.
<context:component-scan base-package="org.apache.cxf.fediz.service.idp.beans" />
Now the JSP views and HTML resources like images will be made available for the spring web flow:
<mvc:resources mapping="/images/**" location="/resources/images/" />
<mvc:view-controller path="/" view-name="index" />
<mvc:view-controller path="/federation/up/login" view-name="signinform" />

<bean id="viewResolver"
 class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 <property name="prefix" value="/WEB-INF/views/" />
 <property name="suffix" value=".jsp" />
</bean>

<bean id="viewFactoryCreator"
 class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
 <property name="viewResolvers">
  <list>
   <ref local="viewResolver" />
  </list>
 </property>
</bean>

<bean id="expressionParser"
 class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser" />

<webflow:flow-builder-services id="builder"
 view-factory-creator="viewFactoryCreator" expression-parser="expressionParser" />
At next the actual spring web flows will get registered. All beans setup within this spring config will be available within the web flows.
<webflow:flow-registry id="flowRegistry"
    flow-builder-services="builder">
    <webflow:flow-location
        path="/WEB-INF/flows/federation-validate-request.xml" id="federation" />
    <webflow:flow-location
        path="/WEB-INF/flows/federation-validate-request.xml" id="federation/up" />
    <webflow:flow-location
        path="/WEB-INF/flows/federation-validate-request.xml" id="federation/krb" />
    <webflow:flow-location
        path="/WEB-INF/flows/federation-validate-request.xml" id="federation/clientcert" />
    <webflow:flow-location 
        path="/WEB-INF/flows/federation-signin-request.xml" id="signinRequest" />
    <webflow:flow-location 
        path="/WEB-INF/flows/federation-signin-response.xml" id="signinResponse" />
</webflow:flow-registry>
Adding security restrictions to the flow will allow spring security to be included in the flow for user authentication.
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping" p:flowRegistry-ref="flowRegistry"
    p:order="2">
</bean>

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    <webflow:flow-execution-attributes>
        <webflow:always-redirect-on-pause value="false" />
    </webflow:flow-execution-attributes>

    <webflow:flow-execution-listeners>
        <webflow:listener ref="securityFlowExecutionListener" />
    </webflow:flow-execution-listeners>
</webflow:flow-executor>

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter" p:flowExecutor-ref="flowExecutor" />

<bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener">
    <property name="accessDecisionManager" ref="accessDecisionManager" />
</bean>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="decisionVoters">
        <list>
            <bean class="org.springframework.security.access.vote.RoleVoter">
                <property name="rolePrefix" value="ROLE_" />
            </bean>
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
        </list>
    </property>
</bean>
The spring security configuration can be found within the WEB-INF/security-config.xml file.

No comments:

Post a Comment