Results 1 to 10 of 10

Thread: How are Grails applications loaded/bootstrapped?

  1. #1

    Question Struggling with how Grails applications are loaded/bootstrapped

    I'm struggling with configuring Spring Security CAS for Grails; I believe the cause of my troubles is (my lack of understanding of) how Grails applications are bootstrapped/loaded. I tried two different methods of configuring Spring Security CAS, both failed at run time; I've described both approaches below. I'd appreciate help in understanding why I'm getting the following errors respectively.

    First approach: Placed the security configurations in "resources.xml", but that led to the following error (see full debug log):
    org.springframework.beans.factory.NoSuchBeanDefini tionException: No bean named 'springSecurityFilterChain' is defined
    Second approach: Placed the security configurations in "applicationContext-security.xml" and loaded it as context-param in web.xml, but that led to the following error:
    context.GrailsContextLoader Error executing bootstraps: Unexpected exception parsing XML document from file /data/workspace/facility/trunk/facility-web/./web-app/WEB-INF/classes/spring/applicationContext-security.xml]; nested exception is java.lang.IllegalStateException: AuthenticationManager has already been registered!
    Thanks!
    Last edited by dan0; Aug 6th, 2010 at 01:58 PM.

  2. #2
    Join Date
    Jul 2007
    Posts
    123

    Default

    You don't want to touch applicationContext-security.xml - that's the parent context; your beans should go in resources.xml or resources.groovy. It's impossible to know what's wrong without seeing your web.xml additions and what you're adding to resources.xml.

    It'd be a lot easier to just use the Spring Security Core and CAS plugins though. They should be customizable for whatever configuration you need.

  3. #3

    Question

    Quote Originally Posted by burtbeckwith View Post
    You don't want to touch applicationContext-security.xml - that's the parent context; your beans should go in resources.xml or resources.groovy. It's impossible to know what's wrong without seeing your web.xml additions and what you're adding to resources.xml.
    Below is the web.xml file. Most of the configurations are defaults; I added: Spring Security Filter, Spring Security Filter Mapping and Spring Security concurrent session management listener.

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4"
             xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
        <display-name>/@grails.project.key@</display-name>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/applicationContext.xml</param-value>
        </context-param>
    
        <context-param>
            <param-name>webAppRootKey</param-name>
            <param-value>@grails.project.key@</param-value>
        </context-param>
        
        <!-- Spring Security Filter -->
        <filter>
            <filter-name>springSecurityFilterChain</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
    
        <filter>
            <filter-name>sitemesh</filter-name>
            <filter-class>org.codehaus.groovy.grails.web.sitemesh.GrailsPageFilter</filter-class>
        </filter>
    
        <filter>
            <filter-name>charEncodingFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <init-param>
                <param-name>targetBeanName</param-name>
                <param-value>characterEncodingFilter</param-value>
            </init-param>
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        
        <!-- Spring Security Filter Mapping -->
        <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <filter-mapping>
            <filter-name>charEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <filter-mapping>
            <filter-name>sitemesh</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <!-- Spring Security concurrent session management listener -->
        <listener>
      	    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
        </listener>
    
        <listener>
            <listener-class>org.codehaus.groovy.grails.web.util.Log4jConfigListener</listener-class>
        </listener>
    
        <listener>
            <listener-class>org.codehaus.groovy.grails.web.context.GrailsContextLoaderListener</listener-class>
        </listener>
    
        <!-- Grails dispatcher servlet -->
        <servlet>
            <servlet-name>grails</servlet-name>
            <servlet-class>org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!-- The Groovy Server Pages servlet -->
        <servlet>
            <servlet-name>gsp</servlet-name>
            <servlet-class>org.codehaus.groovy.grails.web.pages.GroovyPagesServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>gsp</servlet-name>
            <url-pattern>*.gsp</url-pattern>
        </servlet-mapping>
    
        <welcome-file-list>
            <!--
            The order of the welcome pages is important.  JBoss deployment will
            break if index.gsp is first in the list.
            -->
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>index.gsp</welcome-file>
        </welcome-file-list>
    
        <jsp-config>
            <taglib>
                <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
                <taglib-location>/WEB-INF/tld/c.tld</taglib-location>
            </taglib>
            <taglib>
                <taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>
                <taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
            </taglib>
            <taglib>
                <taglib-uri>http://www.springframework.org/tags</taglib-uri>
                <taglib-location>/WEB-INF/tld/spring.tld</taglib-location>
            </taglib>
            <taglib>
                <taglib-uri>http://grails.codehaus.org/tags</taglib-uri>
                <taglib-location>/WEB-INF/tld/grails.tld</taglib-location>
            </taglib>
        </jsp-config>
    
    </web-app>
    Below is the resources.xml file. I have custom implementations of UserDetails & UserDetailsService (i.e. FacilityUserDetailsService, it is loaded via component scanning).
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:sec="http://www.springframework.org/schema/security"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.0.xsd">
     
        <context:annotation-config/>
        <context:component-scan base-package="com.facility.web.security"/>
        
        <bean id="casTicketValidator" class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
            <constructor-arg index="0" value="http://localhost:8080/facility-cas"/>
        </bean>
        
        <bean id="casService" class="org.springframework.security.cas.ServiceProperties">
            <property name="service" value="http://localhost:9999/facility-web/sign-in"/>
            <property name="sendRenew" value="false"/>
        </bean>
        
        <bean id="casAuthEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
            <property name="loginUrl" value="http://localhost:8080/facility-cas/login"/>
            <property name="serviceProperties" ref="casService"/>
        </bean>
        
        <bean id="authenticationUserDetailsService" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" depends-on="facilityUserDetailsService">
            <constructor-arg ref="facilityUserDetailsService"/>
        </bean>
        
        <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
            <property name="ticketValidator" ref="casTicketValidator"/>
            <property name="serviceProperties" ref="casService"/>
            <property name="key" value="facility-cas"/>
            <property name="authenticationUserDetailsService" ref="facilityUserDetailsService"/>
        </bean>
        
        <sec:authentication-manager alias="authenticationManager">
            <sec:authentication-provider ref="casAuthenticationProvider"/>
        </sec:authentication-manager>
        
        <bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
            <property name="authenticationManager" ref="authenticationManager"/>
            <property name="filterProcessesUrl" value="/facility-web/sign-in"/>
        </bean>
        
        <sec:http auto-config="true" entry-point-ref="casAuthEntryPoint">
            <sec:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/>
            <sec:intercept-url pattern="/register" filters="none"/>
            <sec:intercept-url pattern="/**" access="ROLE_USER, ROLE_SUPER_USER, ROLE_ADMIN"/>
        </sec:http> 
           
        <bean id="webClient" class="org.apache.cxf.jaxrs.client.WebClient" factory-method="create">
            <constructor-arg type="java.lang.String" value="http://localhost:8080"/>
        </bean>
    
        <bean id="validatorFactory" class="javax.validation.Validation" factory-method="buildDefaultValidatorFactory"/>
        
        <bean id="validator" factory-bean="validatorFactory" factory-method="getValidator"/>
        
    </beans>
    Quote Originally Posted by burtbeckwith View Post
    It'd be a lot easier to just use the Spring Security Core and CAS plugins though. They should be customizable for whatever configuration you need.
    Can I use the Spring Security Core plugin with my custom java implementations of UserDetails and UserDetailsService? If yes, would I still need to create user and role classes via:
    Code:
    grails s2-quickstart com.myapp User Roles
    While I plan on using the plugins, I'm still interested in learning what I was doing wrong in my previous (two) approaches, so if you can discern the cause of my problems kindly share. Thanks.
    Last edited by dan0; Aug 10th, 2010 at 08:09 PM.

  4. #4
    Join Date
    Jun 2010
    Location
    London
    Posts
    304

    Default

    Not sure about the second problem. It looks like double-loading of Spring beans, perhaps due to a cached copy of the old applicationContext.xml.

    Your first problem simply seems to be that the springSecurityFilterChain bean isn't defined anywhere. I don't see it in resources.xml. How is it supposed to be defined?

  5. #5

    Default

    Quote Originally Posted by pledbrook View Post
    Your first problem simply seems to be that the springSecurityFilterChain bean isn't defined anywhere. I don't see it in resources.xml. How is it supposed to be defined?
    I'm relying on the security namespace to create the 'springSecurityFilterChain' bean:

    Code:
    <sec:http auto-config="true" entry-point-ref="casAuthEntryPoint">
            <sec:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/>
            <sec:intercept-url pattern="/register" filters="none"/>
            <sec:intercept-url pattern="/**" access="ROLE_USER, ROLE_SUPER_USER, ROLE_ADMIN"/>
    </sec:http>

  6. #6
    Join Date
    Jul 2007
    Posts
    123

    Default

    Sorry, you were right about creating an applicationContext-security.xml. I misread that (even though I copy/pasted it ...) as applicationContext.xml which you shouldn't edit. But the only way I could get this to work was to refer to a second app context xml file from web.xml in the contextConfigLocation attribute. I put it in WEB-INF along with applicationContext.xml:

    Code:
    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>
          /WEB-INF/applicationContext.xml
          /WEB-INF/applicationContext-security.xml
       </param-value>
    </context-param>
    I was also missing the config jar so I added that to the Ivy config in BuildConfig.groovy (you could also just copy the jars to the lib dir):

    Code:
    grails.project.class.dir = 'target/classes'
    grails.project.test.class.dir = 'target/test-classes'
    grails.project.test.reports.dir = 'target/test-reports'
    
    grails.project.dependency.resolution = {
    
       inherits 'global'
       log 'warn'
    
       repositories {
          grailsPlugins()
          grailsHome()
          grailsCentral()
          ebr() // SpringSource  http://www.springsource.com/repository
       }
    
       dependencies {
          runtime('org.springframework.security:org.springframework.security.core:3.0.3.RELEASE') {
             excludes 'com.springsource.org.aopalliance',
                      'com.springsource.org.apache.commons.logging',
                      'org.springframework.beans',
                      'org.springframework.context',
                      'org.springframework.core'
          }
    
          runtime('org.springframework.security:org.springframework.security.config:3.0.3.RELEASE')
    
          runtime('org.springframework.security:org.springframework.security.web:3.0.3.RELEASE') {
             excludes 'com.springsource.javax.servlet',
                      'com.springsource.org.aopalliance',
                      'com.springsource.org.apache.commons.logging',
                      'org.springframework.aop',
                      'org.springframework.beans',
                      'org.springframework.context',
                      'org.springframework.core',
                      'org.springframework.web'
          }
    
          runtime('org.springframework.security:org.springframework.security.cas:3.0.3.RELEASE') {
             excludes 'com.springsource.javax.servlet',
                      'com.springsource.org.aopalliance',
                      'com.springsource.org.apache.commons.logging',
                      'com.springsource.org.apache.xmlcommons',
                      'org.springframework.aop',
                      'org.springframework.beans',
                      'org.springframework.context',
                      'org.springframework.core',
                      'org.springframework.transaction',
                      'org.springframework.web'
          }
       }
    }

  7. #7
    Join Date
    Jun 2010
    Location
    London
    Posts
    304

    Default

    Quote Originally Posted by dan0 View Post
    I'm relying on the security namespace to create the 'springSecurityFilterChain' bean:

    Code:
    <sec:http auto-config="true" entry-point-ref="casAuthEntryPoint">
            <sec:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/>
            <sec:intercept-url pattern="/register" filters="none"/>
            <sec:intercept-url pattern="/**" access="ROLE_USER, ROLE_SUPER_USER, ROLE_ADMIN"/>
    </sec:http>
    Would you be able to zip up an example project that demonstrates the issue and attach it to a JIRA issue? I can't see why it wouldn't work, unless the custom element is doing something unusual.

  8. #8

    Default

    @burtbeckwith: Thanks for the example, from it I was able to figure out that the applicationContext-security.xml needed to be in the web-app/WEB-INF/ directory; I was placing it in the grails-app/conf/spring directory and configuring the web.xml file as:
    Code:
    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>
          /WEB-INF/applicationContext.xml
          classpath:**/applicationContext-security.xml
       </param-value>
    </context-param>
    Relocating the configuration file resolved all errors

    As an aside, do you know the order in which configuration files -- applicationContext.xml, resources.xml, etc. -- are read or where I can find documentation that explains the order?

    @pledbrook: Relocating the applicationContext-security.xml file resolved my errors -- there's no longer a need to create a JIRA issue, right?

  9. #9
    Join Date
    Jun 2010
    Location
    London
    Posts
    304

    Default

    applicationContext.xml is loaded first via the ContextLoaderListener. Then the plugins are loaded (which may add beans via the doWithSpring hook), and only after that are resources.groovy and resources.xml loaded.

    Although your problem is fixed, I'm still wondering why it wasn't working with the configuration defined in resources.xml. So if you don't mind raising an issue so that we can investigate, that would be great. If you don't have time, don't worry.

  10. #10

    Default

    Quote Originally Posted by pledbrook View Post
    applicationContext.xml is loaded first via the ContextLoaderListener. Then the plugins are loaded (which may add beans via the doWithSpring hook), and only after that are resources.groovy and resources.xml loaded.
    Thanks.

    Quote Originally Posted by pledbrook View Post
    Although your problem is fixed, I'm still wondering why it wasn't working with the configuration defined in resources.xml. So if you don't mind raising an issue so that we can investigate, that would be great. If you don't have time, don't worry.
    Sure, I'll shortly zip up a bare-bones example app that demonstrates the issue and attach it to a JIRA issue.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •