Announcement Announcement Module
Collapse
No announcement yet.
How are Grails applications loaded/bootstrapped? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How are Grails applications 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, 01:58 PM.

  • #2
    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.

    Comment


    • #3
      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>
      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, 08:09 PM.

      Comment


      • #4
        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?

        Comment


        • #5
          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>

          Comment


          • #6
            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'
                  }
               }
            }

            Comment


            • #7
              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.

              Comment


              • #8
                @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?

                Comment


                • #9
                  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.

                  Comment


                  • #10
                    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.

                    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.

                    Comment

                    Working...
                    X